rabbitmq进阶:整体理解、编程模型和高级特性
文章目录编程模型RabbitMQ整体理解和队列解析RabbitMQ编程模型原生API编程模型RabbitMQ整体理解和队列解析RabbitMQ是基于AMQP协议开发的一个MQ产品,可以参照下图来理解RabbitMQ当中的基础概念:一些基本概念在Rabbitmq快速入门有讲过主要讲下 新版本rabbitmq的队列。在3.9.15版本中包含了三种队列类型,分别是Classic 经典队列, Quorum
文章目录
编程模型
RabbitMQ整体理解和队列解析
RabbitMQ是基于AMQP协议开发的一个MQ产品,可以参照下图来理解RabbitMQ当中的基础概念:
一些基本概念在Rabbitmq快速入门有讲过
主要讲下 新版本rabbitmq的队列。在3.9.15版本中包含了三种队列类型,分别是Classic 经典队列
, Quorum 仲裁队列
, Stream队列
。
Classic 经典队列
这是RabbitMQ最为经典的队列类型。在单机环境中,拥有比较高的消息可靠性
在这个图中可以看到,经典队列可以选择是否持久化(Durability)以及是否自动删除(Auto delete)两个属性。
其中,Durability有两个选项,Durable和Transient。 Durable表示队列会将消息保存到硬盘,这样消息的安全性更高。但是同时,由于需要有更多的IO操作,所以生产和消费消息的性能,相比Transient会比较低。
Auto delete属性如果选择为是,那队列将在至少一个消费者已经连接,然后所有的消费者都断开连接后删除自己。
后面的Arguments部分,还有非常多的参数,可以点击后面的问号逐步了解。
Quorum 仲裁队列
仲裁队列,是RabbitMQ从3.8.0版本,引入的一个新的队列类型,整个3.8.X版本,也都是在围绕仲裁队列进行完善和优化。仲裁队列相比Classic经典队列,在分布式环境下对消息的可靠性保障更高。官方文档中表示,未来会使用Quorum仲裁队列代替传统Classic队列。
关于Quorum的详细介绍见 https://www.rabbitmq.com/quorum-queues.html
Quorum是基于Raft一致性协议实现的一种新型的分布式消息队列,他实现了持久化,多备份的FIFO队列,主要就是针对RabbitMQ的镜像模式设计的。简单理解就是quorum队列中的消息需要有集群中多半节点同意确认后,才会写入到队列
中。这种队列类似于RocketMQ当中的DLedger集群。这种方式可以保证消息在集群内部不会丢失。同时,Quorum是以牺牲很多高级队列特性为代价,来进一步保证消息在分布式环境下的高可靠。
从整体功能上来说,Quorum队列是在Classic经典队列的基础上做减法,因此对于RabbitMQ的长期使用者而言,其实是会影响使用体验的。他与普通队列的区别:
从官方这个比较图就能看到,Quorum队列大部分功能都是在Classic队列基础上做减法,比如Non-durable queues表示是非持久化的内存队列。Exclusivity表示独占队列,即表示队列只能由声明该队列的Connection连接来进行使用,包括队列创
建、删除、收发消息等,并且独占队列会在声明该队列的Connection断开后自动删除。
其中有个特例就是这个Poison Message(有毒的消息)。所谓毒消息是指消息一直不能被消费者正常消费(可能是由于消费者失败或者消费逻辑有问题等),就会导致消息不断的重新入队,这样这些消息就成为了毒消息。这些毒消息应该有保障机制进行标记并及时删除。Quorum队列会持续跟踪消息的失败投递尝试次数,并记录在"x-delivery-count"这样一个头部参数中。然后,就可以通过设置 Delivery limit参数来定制一个毒消息的删除策略。当消息的重复投递次数超过了Delivery limit参
数阈值时,RabbitMQ就会删除这些毒消息。当然,如果配置了死信队列的话,就会进入对应的死信队列。
Quorum队列更适合于队列长期存在,并且对容错、数据安全方面的要求比低延迟、不持久等高级队列更能要求更严格的场景。例如 电商系统的订单,引入MQ后,处理速度可以慢一点,但是订单不能丢失。
也对应以下一些不适合使用的场景:
- 一些临时使用的队列:比如transient临时队列,exclusive独占队列,或者经常会修改和删除的队列。
- 对消息低延迟要求高: 一致性算法会影响消息的延迟。
- 对数据安全性要求不高:Quorum队列需要消费者手动通知或者生产者手动确认。
- 队列消息积压严重 : 如果队列中的消息很大,或者积压的消息很多,就不要使用Quorum队列。Quorum队列当前会将所有消息始终保存在内存中,直到达到内存使用极限。
Stream队列
Stream队列是RabbitMQ自3.9.0版本开始引入的一种新的数据队列类型,也是目前官方最为推荐的队列类型。这种队列类型的消息是持久化到磁盘并且具备分布式备份的,更适合于消费者多,读消息非常频繁的场景。
Stream队列的官方文档地址: https://www.rabbitmq.com/streams.html
Stream队列的核心是以append-only只添加的日志来记录消息,整体来说,就是消息将以append-only的方式持久化到日志文件中,然后通过调整每个消费者的消费进度offset,来实现消息的多次分发。下方有几个属性也都是来定义日志文件的大
小以及保存时间。如果你熟悉Kafka或者RocketMQ,会对这种日志记录消息的方式非常熟悉。这种队列提供了RabbitMQ已有的其他队列类型不太好实现的四个特点:
- large fan-outs 大规模分发
当想要向多个订阅者发送相同的消息时,以往的队列类型必须为每个消费者绑定一个专用的队列。如果消费者的数量很大,这就会导致性能低下。而Stream队列允许任意数量的消费者使用同一个队列的消息,从而消除绑定多个队列的需求。 - Replay/Time-travelling 消息回溯
RabbitMQ已有的这些队列类型,在消费者处理完消息后,消息都会从队列中删除,因此,无法重新读取已经消费过的消息。而Stream队列允许用户在日志的任何一个连接点开始重新读取数据 - Throughput Performance 高吞吐性能
Strem队列的设计以性能为主要目标,对消息传递吞吐量的提升非常明显 - Large logs 大日志
RabbitMQ一直以来有一个让人诟病的地方,就是当队列中积累的消息过多时,性能下降会非常明显。但是Stream队列的设计目标就是以最小的内存开销高效地存储大量的数据。
整体上来说,RabbitMQ的Stream队列,其实有很多地方借鉴了其他MQ产品的优点,在保证消息可靠性的基础上,着力提高队列的消息吞吐量以及消息转发性能。因此,Stream也是在视图解决一个RabbitMQ一直以来,让人诟病的缺点,就
是当队列中积累的消息过多时,性能下降会非常明显的问题。RabbitMQ以往更专注于企业级的内部使用,但是从这些队列功能可以看到,Rabbitmq也在向更复杂的互联网环境靠拢,未来对于RabbitMQ的了解,也需要随着版本推进,不断更新。
RabbitMQ编程模型
RabbitMQ的使用生态已经相当庞大,支持非常多的语言。而就以java而论,也已经支持非常多的扩展。接下来会从原生API、SpringBoot集成、SpringCloudStream集成,三个角度来详细学习RabbitMQ的编程模型。
原生API
使用RabbitMQ提供的原生客户端API进行交互。先来了解下如何使用Classic和Quorum队列。至于Stream队列,目前他使用的是和这两个队列不同的客户端,所以会在后面一个章节单独讨论。
maven依赖
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.9.0</version>
</dependency>
创建连接,获取Channel
public class RabbitMQUtil {
private static Connection connection;
private static final String HOST_NAME="127.0.0.1";
private static final int HOST_PORT=5672;
private RabbitMQUtil() {}
public static Connection getConnection() throws Exception {
if(null == connection) {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST_NAME);
factory.setPort(HOST_PORT);
factory.setUsername("admin");
factory.setPassword("admin");
factory.setVirtualHost("/test");
connection = factory.newConnection();
}
return connection;
}
}
声明queue队列
关键代码:
channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments);
如果要声明一个Quorum队列,则只需要在后面的arguments中传入一个参数,x-queue-type
,参数值设定为quorum。
对于Quorum类型,durable参数就必须是true了,设置成false的话,会报错。同样,exclusive参数必须设置为false
如果要声明一个Stream队列,则 x-queue-type参数要设置为 stream,同样,durable参数必须是true,exclusive必须是false
x-max-length-bytes
表示日志文件的最大字节数。x-stream-maxsegment-size-bytes
每一个日志文件的最大大小。这两个是可选参数,通常为了防止stream日志无限制累计,都会配合stream队列一起声明
public class Sender {
private static final String QUEUE_NAME = "hello";
public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
Map<String,Object> params = new HashMap<>();
//声明Quorum队列需要的参数
// params.put("x-queue-type","quorum");
//声明stream队列需要的参数
// 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
//声明其他类型队列的方式就是添加一个x-queue-type参数。默认是classic
channel.queueDeclare(QUEUE_NAME, true, false, false, params);
//生产发送消息
String message = "Hello World!test";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'");
channel.close();
connection.close();
}
}
声明的队列,如果服务端没有,那么会自动创建。但是如果服务端有了这个队列,那么声明的队列属性必须和服务端的队列属性一致才行。
Producer根据应用场景发送消息到queue
关键代码:
channel.basicPublish(String exchange, String routingKey, BasicProperties props,message.getBytes("UTF-8")) ;
Consumer消费消息
定义消费者,消费消息进行处理,并向RabbitMQ进行消息确认。确认了之后就表明这个消息已经消费完了,否则RabbitMQ还会继续让别的消费者实例来处理。
消费者消费主要有两种模式:
一种是主动消费模式,Comsumer主动到rabbitMQ服务器上去获取指定的messge进行消费
一种是被动消费模式,Consumer等待rabbitMQ 服务器将message推送过来再消费。一般是启一个一直挂起的线程来等待。
主动消费模式
关键代码:
GetResponse response = channel.basicGet(QUEUE_NAME, boolean autoAck)
public static void main(String[] argv) throws Exception {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
Map<String,Object> params = new HashMap<>();
// params.put("x-queue-type","quorum");
//声明Quorum队列的方式就是添加一个x-queue-type参数,指定为quorum。默认是classic
channel.queueDeclare(QUEUE_NAME, true, false, false, params);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
GetResponse response = channel.basicGet(QUEUE_NAME, true);
if(null != response){
System.out.println(new String(response.getBody(),"UTF-8"));
}
channel.close();
connection.close();
}
被动消费模式
关键代码:
channel.basicConsume(String queue, boolean autoAck, Consumer callback)
public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//Consumer接口还应该一个实现QueueConsuemr 但是代码注释过期了。
Consumer myconsumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
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);
}
};
channel.basicConsume(QUEUE_NAME, true, myconsumer);
}
其中autoAck是个关键。autoAck为true则表示消息发送到该Consumer后就被Consumer消费掉了,不需要再往其他Consumer转发。为false则会继续往其他Consumer转发。
要注意如果每个Consumer一直为false,会导致消息不停的被转发,不停的吞噬系统资源,最终造成宕机。
Stream队列的消费
public static void main(String[] args) throws Exception {
Connection connection = RabbitMQUtil.getConnection();
Channel channel = connection.createChannel();
//1、这个属性必须设置。
channel.basicQos(100);
//2、声明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);
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);
}
};
//3、消费时,必须指定offset。 可选的值:
// first: 从日志队列中第一个可消费的消息开始消费
// last: 消费消息日志中最后一个消息
// next: 相当于不指定offset,消费不到消息。
// Offset: 一个数字型的偏移量
// Timestamp:一个代表时间的Data类型变量,表示从这个时间点开始消费。例如 一个小时前 Date timestamp = new Date(System.currentTimeMillis() - 60 * 60 * 1_000)
Map<String,Object> consumeParam = new HashMap<>();
consumeParam.put("x-stream-offset","last");
channel.basicConsume(QUEUE_NAME, false,consumeParam, myconsumer);
channel.close();
完成以后关闭连接,释放资源
channel.close();
Publisher Confirms 发送者消息确认
RabbitMQ的消息可靠性是非常高的,但是他以往的机制都是保证消息发送到了MQ之后,可以推送到消费者消费,不会丢失消息。但是发送者发送消息是否成功是没有保证的。我们可以回顾下,发送者发送消息的基础API:Producer.basicPublish
方法是没有返回值的,也就是说,一次发送消息是否成功,应用是不知道的,这在业务上就容易造成消息丢失。而这个模块就是通过给发送者提供一些确认机制,来保证这个消息发送的过程是成功的。
发送者确认模式默认是不开启的,所以如果需要开启发送者确认模式,需要手动在channel中进行声明。
channel.confirmSelect();
在官网的示例中,重点解释了三种策略:
发布确认单条消息
即发布一条消息就确认一条消息,代码:
for (int i = 0; i < MESSAGE_COUNT; i++) {
String body = String.valueOf(i);
channel.basicPublish("", queue, null, body.getBytes());
channel.waitForConfirmsOrDie(5_000);
}
channel.waitForConfirmsOrDie(5_000);这个方法就会在channel端等待RabbitMQ给出一个响应,用来表明这个消息已经正确发送到了RabbitMQ服务端。但是要注意,这个方法会同步阻塞channel,在等待确认期间,channel将不能再继续发送消息,也就是说会明显降低集群的发送速度即吞吐量。
批量消息确认
之前单条确认的机制会对系统的吞吐量造成很大的影响,所以稍微中和一点的方式就是发送一批消息后,再一起确认。
这种方式可以稍微缓解下发送者确认模式对吞吐量的影响。但是也有个固有的问题就是,当确认出现异常时,发送者只能知道是这一批消息出问题了, 而无法确认具体是哪一条消息出了问题。所以接下来就需要增加一个机制能够具体对每一条发送出错的消息进行处理。
异步确认消息
实现的方式也比较简单,Producer在channel中注册监听器来对消息进行确认。核心代码就是一个:
channel.addConfirmListener(ConfirmCallback var1, ConfirmCallback var2)
按说监听只要注册一个就可以了,那为什么这里要注册两个呢?如果对照下RocketMQ的事务消息机制,这就很容易理解了。发送者在发送完消息后,就会执行第一个监听器callback1,然后等服务端发过来的反馈后,再执行第二个监听器
callback2。
然后关于这个ConfirmCallback,这是个监听器接口,里面只有一个方法: void handle(long sequenceNumber, boolean multiple) throws IOException;
这方法中的两个参数,equenceNumer
:这个是一个唯一的序列号,代表一个唯一的消息。应用程序需要自己来将这个序列号与消息对应起来;
multiple
:这个是一个Boolean型的参数。如果是true,就表示这一次只确认了当前一条消息。如果是false,就表示RabbitMQ这一次确认了一批消息,在sequenceNumber之前的所有消息都已经确认完成了。
SpringBoot集成
SpringBoot官方就集成了RabbitMQ,所以RabbitMQ与SpringBoot的集成是非常简单的。不过,SpringBoot集成RabbitMQ的方式是按照Spring的一套统一的MQ模型创建的,因此SpringBoot集成插件中对于生产者、消息、消费者等重要的对象模型,与RabbitMQ原生的各个组件有对应关系,但是并不完全相同。
具体代码示例:
springboot整合rabbitmq
在目前版本下,使用RabbitMQ的SpringBoot框架集成,可以正常声明Stream队列,往Stream队列发送消息,但是无法直接消费Stream队列了。
关于这个问题,还是需要从Stream队列的三个重点操作入手。SpringBoot框架集成RabbitMQ后,为了简化编程模型,就把channel,connection等这些关键对象给隐藏了,目前框架下,无法直接接入这些对象的注入过程,所以无法直接使用。
如果非要使用Stream队列,那么有两种方式,一种是使用原生API的方式,在SpringBoot框架下自行封装。另一种是使用RabbitMQ的Stream 插件。在服务端通过Strem插件打开TCP连接接口,并配合单独提供的Stream客户端使用。这种方式对应用端的影响太重了,并且并没有提供与SpringBoot框架的集成,还需要自行完善,因此选择使用的企业还比较少。
SpringCloudStream集成
SpringCloudStream 是一个构建高扩展和事件驱动的微服务系统的框架,用于连接共有消息系统。
官网地址: https://spring.io/projects/spring-cloud-stream。
整体上是把各种花里胡哨的MQ产品抽象成了一套非常简单的统一的编程框架,以实现事件驱动的编程模型。社区官方实现了RabbitMQ,Apache Kafka,Kafka,Stream和Amazon Kinesis这几种产品,而其他还有很多产品比如RocketMQ,都
是由产品方自行提供扩展实现。所以可以看到,对于RabbitMQ,使用SpringCloudStream框架算是一种比较成熟的集成方案。但是需要主要注意的是,SpringCloudStream框架集成的版本通常是比RabbitMQ落后几个版本的,使用时
需要注意。
代码示例:springCloudStream集成rabbitmq
高级特性
部分内容在 SpringCloudStream集成和 SpringBoot集成 有对应代码示例
Headers路由
direct,fanout,topic等这些Exchange,都是以routingkey为关键字来进行消息路由的,但是这些Exchange有一个普遍的局限就是都是只支持一个字符串的形式,而不支持其他形式。Headers类型的Exchange就是一种忽略routingKey的路由方式。他通过Headers来进行消息路由。
这个headers是一个键值对,发送者可以在发送的时候定义一些键值对,接受者也可以在绑定时
定义自己的键值对。当键值对匹配时,对应的消费者就能接收到消息。匹配的方式有两种,一种是all,表示需要所有的键值对都满足才行。另一种是any,表示只要满足其中一个键值就可以了。而这个值,可以是List、Boolean等多个类型。
分组消费策略
回顾下RabbitMQ的消费模式,Exchange与Queue之间的消息路由都是通过RoutingKey关键字来进行的,不同类型的Exchange对RoutingKey进行不同的处理。那有没有不通过RoutingKey来进行路由的策略呢?在RabbitMQ产品当中,确实没有这样的路由策略,但是在SpringCloudStream
框架对RabbitMQ进行封装时,提供了一个这种策略,即分区消费策略。
分组消费达到一个什么样的目的呢,就是当有多个消费者实例消费同一个bingding(队列)时,不同的group中,会同样消费同一个Message,而在同一个group中,只会有一个消费者消息到一个Message。
在分组消费策略上有一种相似的策略,叫做分区消费。
死信队列
死信(Dead letter)队列是RabbitMQ中的一个高级功能,所谓死信,就是长期没有人消费的消息。RabbitMQ中有以下几种情况会产生死信:
- 消息被拒绝(basic.reject/baskc.nack)并且设置消息不重新返回队列 (配置
spring.rabbitmq.listener.default-requeue-rejected=true/false
。这个属性默认是true,就是消息处理失败后,就会重新返回队列,后续重新投递。但是这里需要注意,如果队列已经满了,那就会循环不断的报错,这时候就要考虑死信了) - 队列达到最大长度
- 消息TTL过期
在RabbitMQ中,有一类专门处理死信的Exchange交换机和Queue队列。通过RabbitMQ的死信队列功能,可以很好的用来实现延迟队列或者消息补发之类的功能。
RabbitMQ的死信队列实现机制,是在正常队列上声明一个死信交换机dlExchange,然后这个死信交换机dlExchange可以像正常交换机Exchange一样,去绑定队列,分发消息等。
其配置方式,就是在队列中增加声明几个属性来指定死信交换机。而这几个队列属性,即可以在服务器上直接配置,也可以用原生API配置,还可以用SpringBoot的方式声明Queue队列来实现,并且在SCStream框架中也支持定制。主要就是这几个属性:
x-dead-letter-exchange: 对应的死信交换机
x-dead-letter-routing-key: 死信交换机 routing-key
x-message-ttl: 3000 消息过期时间
durable: true 持久化,这个是必须的。
关于死信队列上一篇Rabbitmq快速入门 有讲解过
springboot集成也有案例
优先级消费和流量控制
关于消费队列的优先级,关键是x-max-priority
这个参数,可以指定队列的优先级。默认情况下,RabbitMQ会根据round-robin策略,把消息均匀的给不同的消费者进行处理。但是有了优先级之后,RabbitMQ会保证优先级高的队列先进行消费,而同一优先级的队列,还是会使用round-robin轮询策略来进行分配。
与之对应的是RabbitMQ的流量控制配置,关键是channel.basicQos(int prefetch_size, int prefetch_count, boolean global)
。 这个方法中,prefetch_count
设置了当前消费者节点最多保持的未答复的消息个数,
prefetch_size
设置了当前消费节点最多保持的未答复的消息大小,然后global参数为true则表示该配置针对当前channel的所有队列,而默认情况下是false,表示该配置只针对当前消费者队列。最常用的方式就是只设定一个prefetch_count参数。这两个参数实际上都是为了配置当前消费节点的消息吞吐量。当消费者集群中的业务处理能力或者消息配置不一样时,可以通过给不同的消费节点配置不同的prefetch_count
,再结合消费优先级的配置来实现流量控制策略
懒队列
RabbitMQ从3.6.0版本开始,就引入了懒队列的概念。懒队列会尽可能早的将消息内容保存到硬盘当中,并且只有在用户请求到时,才临时从硬盘加载到RAM内存当中。
懒队列的设计目标是为了支持非常长的队列(数百万级别)。队列可能会因为一些原因变得非常长-也就是数据堆积。
默认情况下,RabbitMQ接收到消息时,会保存到内存以便使用,同时把消息写到硬盘。但是,消息写入硬盘的过程中,是会阻塞队列的。RabbitMQ虽然针对写入硬盘速度做了很多算法优化,但是在长队列中,依然表现不是很理想,所以就有了懒队列的出现。
懒队列会尝试尽可能早的把消息写到硬盘中。这意味着在正常操作的大多数情况下,RAM中要保存的消息要少得多。当然,这是以增加磁盘IO为代价的。
在代码中可以通过x-queue-mode参数指定
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-queue-mode", "lazy");
channel.queueDeclare("myqueue", false, false, false, args);
要注意的是,当一个队列被声明为懒队列,那即使队列被设定为不持久化,消息依然会写入到硬盘中。并且,在镜像集群中,大量的消息也会被同步到当前节点的镜像节点当中,并写入硬盘。这会给集群资源造成很大的负担。
懒队列适合消息量大且长期有堆积的队列,可以减少内存使用,加快消费速度。但是这是以大量消耗集群的网络及磁盘IO为代价的。
远程数据分发插件-Federation Plugin
插件部分内容后续 补充上
消息分片存储插件-Sharding Plugin
更多推荐
所有评论(0)