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

news2024/11/20 3:25:40

一、选择合适的队列.

实际上是可以选择三种队列类型的,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插件,这些碎片队列就尽量不要单独使用了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MyBatis何时使用一级缓存,何时使用二级缓存?

Mybatis设计2级缓存来提升数据检索效率&#xff0c;避免每次都查询数据库。 一、一级缓存 一级缓存 Mybatis 的一级缓存是指 SQLSession&#xff0c;一级缓存的作用域是 SQlSession , Mabits 默认开启一级缓存。 在同一个SqlSession中&#xff0c;执行相同的SQL查询时&#x…

基于STM32CUBEMX驱动TOF模块VL6180与VL6180X(2)----修改测量范围

概述 当使用VL6180传感器进行测距时&#xff0c;可以通过修改缩放因子来改变可测量的距离范围。VL6180是一种基于飞行时间原理的传感器&#xff0c;通过测量光信号的往返时间来确定物体与传感器之间的距离。 默认情况下&#xff0c;VL6180传感器的测距范围约为0至200毫米。然…

显卡检测工具:GPU-Z

今天小编为大家测试了一款轻量级的GPU显卡的测试工具&#xff0c;可以查看GPU的详细信息&#xff0c;以供各位同学们学习。 一、简单介绍 GPU-Z是一款方便实用的软件工具&#xff0c;专门为用户提供视频卡和GPU的详尽信息。它具有轻巧的特点&#xff0c;不需要安装即可使用&am…

2023版智慧高速智慧公路总体建设方案,售前人员必备方案

导读&#xff1a;原文《智慧高速智慧公路总体建设方案》共83页PPT&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 如需获取完整的电子版内容参考学习 您可以关注评论…

【雷达原理】基本雷达方程的推导

基本雷达方程 一、研究目的二、推导过程1、基本雷达方程常用的表达形式2、计算案例3、仿真代码 参考文献 一、研究目的 雷达方程定量地描述了作用距离与雷达参数及目标特性之间的关系。 研究雷达方程主要有以下作用&#xff1a; &#xff08;1&#xff09;根据雷达参数来估算雷…

慕课:笔记

课程链接&#xff1a;直面JavaScript中的30个疑难杂症_JavaScript面试题-慕课网 第二章&#xff1a;数据类型 数据类型是每门编程语言的必修之课&#xff0c;你是否对JavaScript的数据类型和检测存在困惑&#xff0c;本章节将为你揭晓其中的奥秘&#xff0c;让你对数据类型有…

矩阵压缩算法

当矩阵中存在着重复元素时&#xff0c;为了节省空间会采用压缩算法&#xff0c;关键在于原矩阵空间与压缩后数据结构的对应&#xff1b; 1.对称压缩&#xff1a;数据沿对角线对称的情况&#xff1b; 将矩阵压缩为一维数组&#xff0c;数组的长度是&#xff1a; 对于num[n][n…

VMware虚拟机暴露端口至公网方法流程详解

目录 需求背景 解决方法 准备工作 虚拟机ip设置方法 需求背景 一台电脑需要连接另一台电脑上的虚拟机的端口&#xff0c;直接ping是无法ping通的&#xff0c;因为本地虚拟机的端口未暴露至公网。 解决方法 虚拟机&#xff1a;CentOS 7 64 Linux 本机&#xff1a;Window…

C专家编程 —— 运行时数据结构

文章目录 代码和数据段代码与可执行文件中对应的位置可执行文件中的段在内存中的布局加入动态链接库的内存空间布局堆栈段的作用过程活动记录函数调用过程记录举例 static和auto关键字 汇编嵌入C代码 代码和数据 代码和数据的区别可以理解为编译时和运行时的分界线。 代码&…

guacamole 纯web rdp预研:相关JAVA基础

文章目录 guacamole 纯web rdp预研:相关JAVA基础1. pom.xml2 scm标签3 application/octet-stream4. tomcat webapps下war包5 maven-assembly-plugin maven assembly插件介绍什么是assembly&#xff1f; 6. Mavenz中的source插件的使用和注意事项。7. Maven私库安装与配置8. 配置…

深度学习之目标检测R-CNN模型算法流程详解说明(超详细理论篇)

1.R-CNN论文背景 2. R-CNN算法流程 3. R-CNN创新点 一、R-CNN论文背景 论文网址https://openaccess.thecvf.com/content_cvpr_2014/papers/Girshick_Rich_Feature_Hierarchies_2014_CVPR_paper.pdf   RCNN&#xff08;Region-based Convolutional Neural Networks&#xff…

牛客网基础语法81~90题

牛客网基础语法81~90题&#x1f618;&#x1f618;&#x1f618; &#x1f4ab;前言&#xff1a;今天是咱们第九期刷牛客网上的题目。 &#x1f4ab;目标&#xff1a;可以循环嵌套使用熟练&#xff0c;数组的变问题&#xff0c;对数学知识掌握更加清晰。 &#x1f4ab;鸡汤&…

Matplotlib---热力图

1. 热力图 imshow 是 Matplotlib 库中一个函数&#xff0c;主要用于在 Python 中显示图像。它的完整参数列表如下&#xff1a; matplotlib.pyplot.imshow(X, cmapNone, normNone, aspectNone, interpolationNone, alphaNone, vminNone, vmaxNone, originNone, extentNone, sh…

管理类联考——逻辑——知识篇——论证推理——三、假设——haimian

假设 考点分析 假设 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量1132111 主要问法 上述论述是基于以下哪项假设?基于以下哪项假设能使上述推理成立?上述论证依赖于以下哪项假设?得到这一结论的前提条件是? 解题思路 阅读问题&#xff0c;确…

【TCP/IP】多播 - 定义、原理及编程实现(TTL、多播组、收发信息)

目录 多播 多播的原理 多播的数据传输时的特点 TTL 的概念 TTL 和 多播组的配置方法 多播的编程与实现 发送者 接收者 多播 多播是一种介于单播和广播通信之间的技术方式&#xff0c;可以将发送者所需要发送的数据包分别发送给分散在不同子网中的一组接收者。 多播的原…

分布式软件架构——事务ACID

事务概念 事务处理几乎是每一个信息系统中都会涉及到的问题&#xff0c;它存在的意义就是保证系统中的数据是正确的&#xff0c;不同数据间不会产生矛盾&#xff0c;也就是保证数据状态的一致性&#xff08;Consistency&#xff09; 关于一致性&#xff0c;我们重点关注的是数…

ElasticSearch-安装Head可视化插件

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

理解redis的多线程和IO多路复用

参考资料 https://blog.csdn.net/TZ845195485/article/details/119745735 Redis单线程和多线程问题的背景 Redis里程碑版本迭代 Redis的单线程 主要是指Redis的网络IO和键值对读写是由一个线程来完成的&#xff0c;Redis在处理客户端的请求时包括获取&#xff08;socket读&a…

「实验记录」MIT 6.824 KVRaft Lab3A Without Log Compaction

#Lab3A - KVRaft without log compaction I. SourceII. My CodeIII. MotivationIV. SolutionS1 - client请求S2 - server回应 V. Result I. Source MIT-6.824 2020 课程官网Lab3: KVRaft 实验主页simviso 精品付费翻译 MIT 6.824 课程Paper - Raft extended version II. My C…

Python顺序结果、选择结构、循环结构(超详细讲解+多段代码案例)

我本微末凡尘&#xff0c;可也心向天空 文章目录 一、顺序结构 二、选择结构 1.if----elif----else语句 2.条件表达式 三、循环结构 1.range函数 2.while 循环 3. for----in 循环 四、continue、break在循环中的使用 大家好&#xff0c;我是纪宁 今天要介绍的是python…