rabbitmq第三课-RabbitMQ高级功能详解以及常用插件实战

一、选择合适的队列.

实际上是可以选择三种队列类型的,classic经典队列,Quorum仲裁队列,Stream流式队列。
后面这两种队列也是RabbitMQ在最近的几个大的版本中推出的新的队列类型。3.8.x推出了Quorum仲裁队列,3.9.x推出了Stream流式队列。这些新的队列类型都是RabbitMQ针对现代新的业务场景做出的大的改善。最明显的,以往的RabbitMQ版本,如果消息产生大量积累就会严重影响消息收发的性能。而这两种新的队列可以极大的提升RabbitMQ的消息堆积性能。

1、Classic经典队列

这是RabbitMQ最为经典的队列类型。在单机环境中,拥有比较高的消息可靠性。
在这里插入图片描述
在这个图中可以看到,经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。

其中,Durability有两个选项,Durable和Transient。 Durable表示队列会将消息保存到硬盘,这样消息的安全性更高。但是同时,由于需要有更多的IO操作,所以生产和消费消息的性能,相比Transient会比较低。

Auto delete属性如果选择为是,那队列将在至少一个消费者已经连接,然后所有的消费者都断开连接后删除自己。

2、Quorum仲裁队列

仲裁队列,是RabbitMQ从3.8.0版本,引入的一个新的队列类型,整个3.8.X版本,也都是在围绕仲裁队列进行完善和优化。仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。官方文档中表示,未来会使用Quorum仲裁队列代替传统Classic队列。

在这里插入图片描述
Quorum是基于Raft一致性协议实现的一种新型的分布式消息队列,他实现了持久化,多备份的FIFO队列,主要就是针对RabbitMQ的镜像模式设计的。简单理解就是quorum队列中的消息需要有集群中多半节点同意确认后,才会写入到队列中。

同时,Quorum是以牺牲很多高级队列特性为代价,来进一步保证消息在分布式环境下的高可靠。
从整体功能上来说,Quorum队列是在Classic经典队列的基础上做减法,因此对于RabbitMQ的长期使用者而言,其实是会影响使用体验的。他与普通队列的区别:
在这里插入图片描述
官方这个比较图就能看到,Quorum队列大部分功能都是在Classic队列基础上做减法,比如Nondurable queues表示是非持久化的内存队列。Exclusivity表示独占队列,即表示队列只能由声明该队列的Connection连接来进行使用,包括队列创建、删除、收发消息等,并且独占队列会在声明该队列的Connection断开后自动删除。

其中有个特例就是Poison Message handling(处理有毒的消息)。所谓毒消息是指消息一直不能被消费者正常消费(可能是由于消费者失败或者消费逻辑有问题等),就会导致消息不断的重新入队,这样这些消息就成为了毒消息。
这些毒消息应该有保障机制进行标记并及时删除。Quorum队列会持续跟踪消息的失败投递尝试次数,并记录在"x-delivery-count"这样一个头部参数中。然后,就可以通过设置 Delivery limit参数来定制一个毒消息的删除策略。当消息的重复投递次数超过了Delivery limit参数阈值时,RabbitMQ就会删除这些毒消息。当然,如果配置了死信队列的话,就会进入对应的死信队列。

3、Stream流式队列

Stream队列是RabbitMQ自3.9.0版本开始引入的一种新的数据队列类型。这种队列类型的消息是持久化到磁盘并且具备分布式备份的,更适合于消费者多,读消息非常频繁的场景
在这里插入图片描述
Stream队列的核心是以append-only只添加的日志来记录消息,整体来说,就是消息将以append-only的方式持久化到日志文件中,然后通过调整每个消费者的消费进度offset,来实现消息的多次分发。下方有几个属性也都是来定义日志文件的大小以及保存时间。如果你熟悉Kafka或者RocketMQ,会对这种日志记录消息的方式非常熟悉。

  1. large fan-outs 大规模分发
    当想要向多个订阅者发送相同的消息时,以往的队列类型必须为每个消费者绑定一个专用的队列。如果消费者的数量很大,这就会导致性能低下。而Stream队列允许任意数量的消费者使用同一个队列的消息,从而消除绑定多个队列的需求。
  2. Replay/Time-travelling 消息回溯
    RabbitMQ已有的这些队列类型,在消费者处理完消息后,消息都会从队列中删除,因此,无法重新读取已经消费过的消息。而Stream队列允许用户在日志的任何一个连接点开始重新读取数据。
  3. Throughput Performance 高吞吐性能
    Strem队列的设计以性能为主要目标,对消息传递吞吐量的提升非常明显。
  4. Large logs 大日志
    RabbitMQ一直以来有一个让人诟病的地方,就是当队列中积累的消息过多时,性能下降会非常明显。但是Stream队列的设计目标就是以最小的内存开销高效地存储大量的数据。使用Stream队列可以比较轻松的在队列中积累百万级别的消息。

4、如何使用不同类型的队列

1、Quorum队列

Quorum队列与Classic队列的使用方式是差不多的。最主要的差别就是在声明队列时有点不同。
如果要声明一个Quorum队列,则只需要在后面的arguments中传入一个参数,x-queue-type,参数值设定为quorum。

Map<String,Object> params = new HashMap<>();
		params.put("x-queue-type","quorum");
		channel.queueDeclare(QUEUE_NAME, true, false, false, params);

Quorum队列的消息是必须持久化的,所以durable参数必须设定为true,如果声明为false,就会报错。同样,exclusive参数必须设置为false。这些声明,在Producer和Consumer中是要保持一致的。

2、Stream队列

如果要声明一个Stream队列,则 x-queue-type参数要设置为 stream 。

//声明Stream队列的方式。
		Map<String,Object> params = new HashMap<>();
		params.put("x-queue-type","stream");
		params.put("x-max-length-bytes", 20_000_000_000L); // maximum stream size: 20 GB
		params.put("x-stream-max-segment-size-bytes", 100_000_000); // size of segment files: 100 MB
		channel.queueDeclare(QUEUE_NAME, true, false, false, params);

与Quorum队列类似, Stream队列的durable参数必须声明为true,exclusive参数必须声明为false。

这其中,x-max-length-bytes 表示日志文件的最大字节数。x-stream-max-segment-size-bytes 每一个日志文件的最大大小。这两个是可选参数,通常为了防止stream日志无限制累计,都会配合stream队列一起声明。

然后,当要消费Stream队列时,要重点注意他的三个必要的步骤:

  • channel必须设置basicQos属性。 与Spring框架集成使用时,channe对象可以在@RabbitListener声明的消费者方法中直接引用,Spring框架会进行注入。
  • 正确声明Stream队列。 在Queue对象中传入声明Stream队列所需要的参数。
  • 消费时需要指定offset。 与Spring框架集成时,可以通过注入Channel对象,使用原生API传入offset属性。

例如用原生API创建Stream类型的Consumer时,还必须添加一个参数x-stream-offset,表示从队列的哪个位置开始消费。

Map<String,Object> consumeParam = new HashMap<>();
consumeParam.put("x-stream-offset","last");
channel.basicConsume(QUEUE_NAME, false,consumeParam, myconsumer);

x-stream-offset的可选值有以下几种:

  • first: 从日志队列中第一个可消费的消息开始消费
  • last: 消费消息日志中最后一个消息
  • next: 相当于不指定offset,消费不到消息。
  • Offset: 一个数字型的偏移量
  • Timestamp:一个代表时间的Data类型变量,表示从这个时间点开始消费。例如 一个小时前 Date timestamp = new Date(System.currentTimeMillis() - 60 * 60 * 1_000)

二、死信队列

死信队列是RabbitMQ中非常重要的一个特性。简单理解,他是RabbitMQ对于未能正常消费的消息进行的一种补救机制。死信队列也是一个普通的队列,同样可以在队列上声明消费者,继续对消息进行消费处理。
对于死信队列,在RabbitMQ中主要涉及到几个参数。

x-dead-letter-exchange: mirror.dlExchange 对应的死信交换机
x-dead-letter-routing-key: mirror.messageExchange1.messageQueue1 死信交换机routingkey
x-message-ttl: 3000 消息过期时间
durable: true 持久化,这个是必须的

在这里,x-dead-letter-exchange指定一个交换机作为死信交换机,然后x-dead-letter-routing-key指定交换机的RoutingKey。而接下来,死信交换机就可以像普通交换机一样,通过RoutingKey将消息转发到对应的死信队列中。

1、何时会产生死信

有以下三种情况,RabbitMQ会将一个正常消息转成死信

  1. 消息被消费者确认拒绝。消费者把requeue参数设置为true(false),并且在消费后,向RabbitMQ返回拒绝。channel.basicReject或者channel.basicNack。
  2. 消息达到预设的TTL时限还一直没有被消费。
  3. 消息由于队列已经达到最长长度限制而被丢掉

设置TTL有两种方式,一是通过配置策略指定,另一种是给队列单独声明TTL
策略配置方式 - Web管理平台配置 或者 使用指令配置 60000为毫秒单位

rabbitmqctl set_policy TTL “.*” ‘{“message-ttl”:60000}’ --apply-to queues

在声明队列时指定 - 同样可以在Web管理平台配置,也可以在代码中配置:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);

2、死信队列的配置方式

RabbitMQ中有两种方式可以声明死信队列,一种是针对某个单独队列指定对应的死信队列。另一种就是以策略的方式进行批量死信队列的配置。
针对多个队列,可以使用策略方式,配置统一的死信队列。

rabbitmqctl set_policy DLX “.*” ‘{“dead-letter-exchange”:“my-dlx”}’ --apply-to queues

针对队列单独指定死信队列的方式主要是之前提到的三个属性。

channel.exchangeDeclare("some.exchange.name", "direct");
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "some.exchange.name");
channel.queueDeclare("myqueue", false, false, false, args);

这些参数,也可以在RabbitMQ的管理页面进行配置。例如配置策略时:在这里插入图片描述
另外,在对队列进行配置时,只有Classic经典队列和Quorum仲裁队列才能配置死信队列

3、关于参数x-dead-letter-routing-key

死信在转移到死信队列时,他的Routing key 也会保存下来。但是如果配置了x-dead-letter-routing-key这个参数的话,routingkey就会被替换为配置的这个值。
另外,死信在转移到死信队列的过程中,是没有经过消息发送者确认的,所以并不能保证消息的安全性。

4、如何确定一个消息是不是死信

消息被作为死信转移到死信队列后,会在Header当中增加一些消息。在官网的详细介绍中,可以看到很多内容,比如时间、原因(rejected,expired,maxlen)、队列等。然后header中还会加上第一次成为死信的三个属性,并且这三个属性在以后的传递过程中都不会更改。

  • x-first-death-reason
  • x-first-death-queue
  • x-first-death-exchange

5、基于死信队列实现延迟队列

其实从前面的配置过程能够看到,所谓死信交换机或者死信队列,不过是在交换机或者队列之间建立一种死信对应关系,而死信队列可以像正常队列一样被消费。他与普通队列一样具有FIFO的特性。对死信队列的消费逻辑通常是对这些失效消息进行一些业务上的补偿。
RabbitMQ中,是不存在延迟队列的功能的,而通常如果要用到延迟队列,就会采用TTL+死信队列的方式来处理

三、懒队列

懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。
懒队列的设计目标是为了支持非常长的队列(数百万级别)。队列可能会因为一些原因变得非常长-也就是数据堆积。

  • 消费者服务宕机了
  • 有一个突然的消息高峰,生产者生产消息超过消费者
  • 消费者消费太慢了
    默认情况下,RabbitMQ接收到消息时,会保存到内存以便使用,同时把消息写到硬盘。但是,消息写入硬盘的过程中,是会阻塞队列的。RabbitMQ虽然针对写入硬盘速度做了很多算法优化,但是在长队列中,依然表现不是很理想,所以就有了懒队列的出现。
    在这里插入图片描述

在代码中可以通过x-queue-mode参数指定

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-queue-mode", "lazy");
channel.queueDeclare("myqueue", false, false, false, args);

设定一个策略,在策略中指定queue-mode 为 lazy。

rabbitmqctl set_policy Lazy “^lazy-queue$” ‘{“queue-mode”:“default”}’ --apply-toqueues

注意的是,当一个队列被声明为懒队列,那即使队列被设定为不持久化,消息依然会写入到硬盘中。如果是在集群模式中使用,这会给集群资源带来很大的负担。

懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的。

四、联邦插件

1、插件的作用

在企业中有很多大型的分布式场景,在这些业务场景下,希望服务也能够同样进行分布式部署。这样即可以提高数据的安全性,也能够提升消息读取的性能。
例如,某大型企业,可能在北京机房和长沙机房分别搭建RabbitMQ服务,然后希望长沙机房需要同步北京机房的消息,这样可以让长沙的消费者服务可以直接连接长沙本地的RabbitMQ,而不用费尽周折去连接北京机房的RabbitMQ服务。这时要如何进行数据同步呢?

2、使用步骤

  1. 启动插件
    RabbitMQ的官方运行包中已经包含了Federation插件。只需要启动后就可以直接使用。
# 确认联邦插件
rabbitmq-plugins list|grep federation
# 启用联邦插件
rabbitmq-plugins.bat enable rabbitmq_federation
# 启用联邦插件的管理平台支持
rabbitmq-plugins.bat enable rabbitmq_federation_management

插件启用完成后,可以在管理控制台的Admin菜单看到两个新增选项 Federation Status和FederationUpstreams。

在这里插入图片描述
Upstream表示是一个外部的服务节点,在RabbitMQ中,可以是一个交换机,也可以是一个队列。他的配置方式是由下游服务主动配置一个与上游服务的链接,然后数据就会从上游服务主动同步到下游服务中。

在这里插入图片描述
接下来我们用本地localhost的RabbitMQ服务来模拟DownStream下游服务,去指向一个192.168.65.112服务器上搭建的RabbitMQ服务,搭建一个联邦交换机Federation Exchange。
在这里插入图片描述
服务的名字Name属性随意,URI指向远程服务器(配置方式参看页面上的示例):amqp://admin:admin@192.168.65.112:5672/
下面的Federated exchanges parameters和Federated queues parameters分别指定Upstream(也就是远程服务器)的Exchange和Queue。如果不指定,就是用和DownStream中相同的Exchange和Queue。如果UpStream里没有,就创建新的Exchange和Queue。

3、配置Federation策略

接下来需要配置一个指向上游服务的Federation策略。在配置策略时可以选择是针对Exchange交换机还是针对Queue队列。配置策略时,同样有很多参数可以选择配置。最简化的一个配置如下:
在这里插入图片描述

4、测试

配置完Upstream和对应的策略后,进入Federation Status菜单就能看到Federation插件的执行情况。状态为running表示启动成功,如果配置出错,则会提示失败原因
在这里插入图片描述
在这里插入图片描述
并且在fed_exchange的详情页中也能够看到绑定关系。这里要注意一下他给出了一个默认的Routing_key。
在这里插入图片描述
接下来就可以尝试在上游服务Worker2的fed_exchange中发送消息,消息会同步到Local本地的联邦交换机中,从而被对应的消费者消费到。

在这里插入图片描述

五、消息分片存储插件

1、插件的作用

Lazy Queue懒队列机制提升了消息在RabbitMQ中堆积的能力,但是最终,消息还是需要消费者处理消化。但是如何在消费者的处理能力有限的前提下提升消费者的消费速度呢?RabbitMQ提供的Sharding插件,就提供了一种思路。

上面的懒队列其实就是针对这个问题的一种解决方案。但是很显然,懒队列的方式属于治标不治本。真正要提升RabbitMQ单队列的吞吐量,还是要从数据也就是消息入手,只有将数据真正的分开存储才行。
RabbitMQ提供的Sharding插件,就是一个可选的方案。他会真正将一个队列中的消息分散存储到不同的节点上,并提供多个节点的负载均衡策略实现对等的读与写功能。

2、使用步骤

1、启用Sharding插件

rabbitmq-plugins enable rabbitmq_sharding

2、配置Sharding策略

在这里插入图片描述
在这里插入图片描述

3、新增带Sharding的Exchange交换机

在这里插入图片描述

4、往分片交换机上发送消息

   public static void main(String[] args) throws Exception{
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.56.10");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/mirror");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        //发送者只管往exchange里发消息,而不用关心具体发到哪些queue里。
        channel.exchangeDeclare(EXCHANGE_NAME, "x-modulus-hash");
        String message = "LOG INFO 44444";
        for(int i = 0 ; i < 10000 ; i ++){
            channel.basicPublish(EXCHANGE_NAME, String.valueOf(i), null, message.getBytes());
        }

        channel.close();
        connection.close();
    }

启动后,就会在RabbitMQ上声明一个sharding_exchange。查看这个交换机的详情,可以看到他的分片情况:
在这里插入图片描述
并且,一万条消息被平均分配到了三个队列当中
在这里插入图片描述

5、消费分片交换机上的消息

数据分片后,还是希望能够像一个普通队列一样消费到完整的数据副本。这时,Sharding插件提供了一种伪队列的消费方式。你可以声明一个名字为 exchangename 的伪队列,然后像消费一个普通队列一样去消费这一系列的碎片队列。

 public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("192.168.56.10");
        factory.setPort(5672);
        factory.setUsername("guest");
        factory.setPassword("guest");
        factory.setVirtualHost("/mirror");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUENAME,true,false,false,null);

        Consumer myconsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope,
                                       AMQP.BasicProperties properties, byte[] body)
                    throws IOException {
                System.out.println("========================");
                String routingKey = envelope.getRoutingKey();
                System.out.println("routingKey >" + routingKey);
                String contentType = properties.getContentType();
                System.out.println("contentType >" + contentType);
                long deliveryTag = envelope.getDeliveryTag();
                System.out.println("deliveryTag >" + deliveryTag);
                System.out.println("content:" + new String(body, "UTF-8"));
                // (process the message components here ...)
                //消息处理完后,进行答复。答复过的消息,服务器就不会再次转发。
                //没有答复过的消息,服务器会一直不停转发。
//				 channel.basicAck(deliveryTag, false);
            }
        };
        //三个分片就需要消费三次。
        //sharding插件的实现原理就是将basicConsume方法绑定到分片队列中连接最少的一个队列上。
        String consumeerFlag1 = channel.basicConsume(QUEUENAME, true, myconsumer);
        System.out.println("c1:"+consumeerFlag1);
        String consumeerFlag2 = channel.basicConsume(QUEUENAME, true, myconsumer);
        System.out.println("c2:"+consumeerFlag2);
        String consumeerFlag3 = channel.basicConsume(QUEUENAME, true, myconsumer);
        System.out.println("c3:"+consumeerFlag3);
    }

3、注意事项

使用Sharding插件后,Producer发送消息时,只需要指定虚拟Exchange,并不能确定消息最终会发往哪一个分片队列。而Sharding插件在进行消息分散存储时,虽然尽量是按照轮询的方式,均匀的保存消息。但是,这并不能保证消息就一定是均匀的。
首先,这些消息在分片的过程中,是没有考虑消息顺序的,这会让RabbitMQ中原本就不是很严谨的消息顺序变得更加雪上加霜。所以,Sharding插件适合于那些对于消息延迟要求不严格,以及对消费顺序没有任何要求的的场景。
然后,Sharding插件消费伪队列的消息时,会从消费者最少的碎片中选择队列。这时,如果你的这些碎片队列中已经有了很多其他的消息,那么再去消费伪队列消息时,就会受到这些不均匀数据的影响。所以,如果使用Sharding插件,这些碎片队列就尽量不要单独使用了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/31547.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络基础知识(九)—— 什么是TelnetS?Telnet Over TLS

文章目录 01 | TelnetS02 | OpenSSL03 | 实现思路服务器处理流程客户端处理流程 04 | 代码实现服务端代码客户端代码编译过程 & 执行结果 前面学习了什么是HTTPS协议&#xff0c;了解了HTTPS的工作原理以及具体的工作流程&#xff0c;了解了HTTP协议和HTTPS协议之间的区别。…

Elasticsearch 安装(Linux)

ElasticSearch 概念安装安装es 后台启动 & 停止启动nohup 记录pid 停止 其他启动错误max number of threads内存不足 Cannot allocate memoryfailed to obtain node locks 概念 ES是一款分布式全文搜索引擎&#xff0c;基于Lucene&#xff0c;进行了二次封装&#xff0c;更…

ElasticSearch-安装Head可视化插件

安装Head可视化插件 首先需要依赖node.js和npm环境 1 安装node.js 官方下载地址:http://nodejs.cn/download/ 下载LTS版本&#xff08;长期稳定版本&#xff09; 安装可以更改安装路径,其余的都是选择 下一步傻瓜是安装 安装成功后如下 命令测试 node -v 查看node的版本 n…

Redis 2023面试5题(二)

一、Redis key没设置过期时间却被redis主动删除了 如果一个 Redis 键没有设置过期时间&#xff0c;那么 Redis 无法判断该键是否应该自动删除。因此&#xff0c;如果一个没有设置过期时间的键被 Redis 主动删除了&#xff0c;可能是以下原因之一&#xff1a; 内存不足&#x…

【论文笔记】BEIT:BERT PRE-TRAINING OF IMAGE TRANSFORMERS

GitHub 1.介绍 1.1 挑战 视觉转换器的输入单元&#xff0c;即图像补丁&#xff0c;没有预先存在的词汇。预测遮罩面片的原始像素往往会在预训练短程依赖性和高频细节上浪费建模能力 1.2 回顾一下Bert的基本架构和流程 输入编码&#xff1a;通过tokenizer将输入的文本中的每…

OceanBase—01(入门篇——使用docker安装OceanBase以及介绍连接OB的几种方式)

OceanBase—01&#xff08;入门篇——使用docker安装OceanBase以及介绍连接OB的几种方式&#xff09; 1. 前言1.1 安装部署参考1.1.1 安装前提1.1.2 参考 1.1 修改数据库用户名密码1.2 总结常见连接命令 2. 安装部署OceanBase2.1 启动 OceanBase 数据库实例2.1.1 拉取镜像并启动…

eclipse (C/C++) 常用设置记录

Eclipse 是一个开放源代码的、基于Java的可扩展开发平台&#xff1b;现公司用其作为开发单片机的IDE&#xff1b;因此记录一下常用的配置方法&#xff1b; 文章目录 零、常用默认快捷键一、高亮相同变量二、修改高亮变量颜色三、在整个工程内搜索某个函数四、切换主题五、改变字…

编译原理笔记16:自下而上语法分析(3)构造 DFA、DFA 对下一步分析的指导(有效项目)

目录 由 NFA 用子集法构造 DFA由 LR(0) 项目直接构造识别活前缀的 DFA构造 DFA求拓广文法 GCLOSURE & GO例&#xff1a; 构造 DFA DFA 指导下一步分析有效项目 看了前面的内容&#xff0c;我们已经了解到&#xff1a;分析表和驱动器算法&#xff0c;是 LR 分析器的核心。 …

基于Java+Swing+Mysql实现图书管理系统V2.0

基于JavaSwingMysql实现图书管理系统V2.0 一、系统介绍二、功能展示1.项目内容2.项目骨架3.数据库表4.主界面5.添加6、修改7、查询8、删除 四、其它1.其他系统实现五.获取源码 一、系统介绍 本系统主要有对图书信息的增删改查操作功能。 项目类型&#xff1a;Java SE项目&…

linux 下查看 USB 设备

文章目录 前言目录内容详解usb11-0:1.01-1.1:1.0 结构图设备信息bDeviceClassversionbusnum & devnumdevbMaxPoweridVendor & idProductproductmanufacturerbcdDevicespeedueventbmAttributesdrivers_autoprobe 前言 在 sysfs 文件系统下&#xff0c;查看 USB 设备&am…

Android 9-SystemUI:(1)启动流程

具体分析(以下代码示例&#xff0c;讲解&#xff0c;都是通过&#xff0c;Android9代码来举例&#xff09; SystemUI,其实是可以看作是一个系统级的服务&#xff0c;也就是SystemUIService, SystemUI的服务启动&#xff0c;要从SystemServer.run()方法入手 main 方法里启动了…

企业级微服务架构实战项目--xx优选3-mq+nacos+es实现上下架

一 nacosmqes实现上下架 1.1 架构图 1.2 工程结构 1.3 核心代码流程 1.3.1 请求product模块 2.修改数据库&#xff0c;推送rabbitmq中 1.3.2 rabbitmq的工具类 1.3.3 search模块中rabbit客户端订阅信息 1.监听器监听信息 2.调用相应的上下架方法 2.1 调用product模块&…

Flink 学习三 Flink 流 process function API

Flink 学习三 Flink 流&process function API 1.Flink 多流操作 1.1.split 分流 (deprecated) 把一个数据流根据数据分成多个数据流 1.2 版本后移除 1.2.分流操作 (使用侧流输出) public class _02_SplitStream {public static void main(String[] args) throws Excep…

uniapp设置滚动条滚动到指定位置

场景&#xff1a;左侧菜单栏&#xff0c;每次切换时&#xff0c;需要右侧商品展示区保持滚动条及页面在最顶部 1.利用scroll-view 中scroll-top属性实现 1.1设置scrollToTop属性为0 data() {return {// 保证每次切换&#xff0c;滚动条位置都在最顶部scrollToTop: 0,}; } 1.…

ansible的剧本(playbook)

一、playbooks 概述以及实例操作 1、playbooks 的组成 playbooks 本身由以下各部分组成 &#xff08;1&#xff09;Tasks&#xff1a;任务&#xff0c;即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 &#xff08;2&#xff09;Variables&#xff1…

iOS 单元测试之常用框架 OCMock 详解 | 京东云技术团队

一、单元测试 1.1 单元测试的必要性 测试驱动开发并不是一个很新鲜的概念了。在日常开发中&#xff0c;很多时候需要测试&#xff0c;但是这种输出是必须在点击一系列按钮之后才能在屏幕上显示出来的东西。测试的时候&#xff0c;往往是用模拟器一次一次的从头开始启动 app&a…

团体程序设计天梯赛-练习集L2篇④

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

模糊聚类在负荷实测建模中的应用(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

VSCode使用CodeWhisperer(AI编程)

安装AWS Toolkit插件&#xff0c;点击侧边插件搜索并安装 2.点击aws ->CodeWhisperer ->Start 3.在下拉菜单中点击Use a personal email to sign up and sign in with AWS Builder ID 4.点Copy Code and Proceed&#xff0c;这会自动复制一些东西 5. 收到提示打开外部网…

【跑实验05】利用CLIP中的图像编码器,如何遍历文件夹中的图像,将图像文件改为28*28的尺寸,然后输出到excel中的每一列,最后一列全都标记为0

文章目录 一、初步实现二、警告信息的解决三、Excel的限制四、尝试解决 一、初步实现 要遍历文件夹中的图像并将其尺寸调整为28x28&#xff0c;并将结果输出到Excel中&#xff0c;可以按照以下步骤进行操作&#xff1a; 首先&#xff0c;确保您已经安装了Pandas库&#xff0c…