rabbitMQ介绍

  1. 引言
    rabbitMQ是一款开源的消息中间件,具备高效、可靠的消息传递机制,能够在高并发环境下保证消息的可靠传递。

  2. 作用

  • 解耦:降低应用程序之间通信依赖的耦合性,上游无需关心请求由哪个下游处理,下游也无需关心上游。
  • 异步:RabbitMQ可以作为异步通信工具,通过将消息暂存于队列中,让消费者在后台异步处理,减轻了生产者的负担。这种方式对于处理大量数据或提高并发量的情况尤其有效。
  • 削峰:在高并发场景下,RabbitMQ可以作为消息缓冲队列,削减峰值流量,避免系统因过载而崩溃。
  • 负载均衡:通过将消息分发到多个消费者节点,RabbitMQ可以实现负载均衡,提高系统的处理能力。
  • 持久化:RabbitMQ提供了持久化机制,可以将消息保存到磁盘上,即使系统崩溃,消息也不会丢失。
  1. 工作原理
    在这里插入图片描述
    消息流转:
    在这里插入图片描述

涉及核心概念

  • broker:接收和分发消息的应用,RabbitMQ Server就是Message Broker
  • connection: producer/ consumer和 broker之间的TCP连接
  • channel: 信道channel是在connection 内部建立的逻辑连接,channel 之间完全隔离,connection极大减少了操作系统创建TCP连接的开销,进行了TCP连接复用。参考 NIO思想
  • exchange: 交换机,消息到达 broker的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到队列中
  • queue: 队列,是 RabbitMQ 的内部对象,用于存储消息。一个队列里的消息将会被消费者争抢进行消费
    在这里插入图片描述
  • routing Key:生产者将消息发送到交换机时携带的一个key,用于指定路由规则。命名一般为一个点号".“分隔的字符串(被点号”."分隔开的每 段独立的字符串称为一个单词 ),如 “com.lucas”
  • binding Key:在绑定交换机和队列时,会指定一个bindingKey,生产者发送消息携带的routingKey会和bindingKey对比,若一致就将消息分发至这个队列
  • vHost 虚拟主机:RabbitMQ服务器可以开设多个虚拟主机,每一个vhost本质上是一个低配版的RabbitMQ服务器,拥有自己的交换机、队列和权限机制,这样就能安全地使用一个RabbitMQ服务器来服务多个应用程序,其中每个vhost服务一个应用程序

编程基本要素:

ConnectionFactory factory = new ConnectionFactory();
factory.setHost(HOST_NAME);
factory.setPort(HOST_PORT);
factory.setUsername(USER_NAME);
factory.setPassword(PASSWORD);
factory.setVirtualHost(VIRTUAL_HOST);
 
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
  1. 命名规范
    在这里插入图片描述

  2. 交换机类型

  • Header Exchange
    性能较差,不推荐使用

  • direct Exchange(直连交换机)
    它会把消息路由到那些RoutingKey BindingKey完全匹配的队列中,只有完全匹配消息才会被转发

  • Fanout Excange(扇出交换机 又称 广播交换机)
    将消息发送至所有的队列

  • Topic Exchange(主题交换机)
    将路由按模式匹配,支持对RoutingKey BindingKey的模糊匹配。用"“和”#“做模糊匹配,其中”“用于匹配一个单词,”#"用于匹配零个或多个单词。

    匹配规则举例:
    “animal.#” 可以匹配 “animal”、“animal.dog”、“animal.dog.son” 或 “animal.mammal.dog.son”
    “animal.*” 可以匹配 “animal.dog”、“animal.cat"等的消息,但不能匹配"animal"或"animal.mammal.dog”
    可运用于 用户行为跟踪系统中监控用户敏感行为

  1. 消息的发送与消费
    发送:
    在这里插入图片描述
String msg= "消息内容";
channel.basicPublish("excange_name","routingKey",null,msg.getBytes(StandardCharsets.UTF_8));

其中 AMQP.BasicProperties参数如下:

public static class BasicProperties extends AMQBasicProperties {
    //消息的内容类型,例如"application/json","text/plain"
    private String contentType;
     //消息内容编码
    private String contentEncoding;
     //消息的header 如存灰度标识x-hll-gray-version trace_id
    private Map<String,Object> headers;
    //消息持久化 1(nopersistent)非持久化,2(persistent)持久化。建议不设置 为null,设置为持久化将会对消息投递和性能有着很大的影响。
    //注意与队列的持久化区分开 一个队列可能包含持久化和未持久化的消息
    private Integer deliveryMode;
    //消息的优先级,0-9,9为最高优先级
    private Integer priority;
    //关联ID,用于关联RPC的请求和响应
    private String correlationId;
    //回复队列,用于指定RPC的回复消息发送到哪个队列 【较少使用】
    private String replyTo;
    //消息的过期时间 消息过期就会变成"死信"消息
    private String expiration;
    //消息唯一标识
    private String messageId;
    //消息的时间戳
    private Date timestamp;
    //消息中的内容类型
    private String type;
    //用户ID
    private String userId;
    //应用程序ID
    private String appId;
    //集群ID
    private String clusterId;
   }

消费:
消息消费包含推模式(push) 和 拉模式(pull)。
其中推模式(默认):
在这里插入图片描述

//主动将消息推送给消费者
channel.basicConsume("queue.name",true,successBack,failedBack);

拉模式:

//主动从消息中间件拉取消息
GetResponse basicGet(String queue, boolean autoAck)

在这里插入图片描述
如果只想从队列中获取单条消息而非持续订阅,则可以使用channel.basicGet方法来进行消息消费
对比:
推模式消息实时性更高,该模式直接从内存中获取消息,能有效的提高消息的处理效率以及吞吐量
拉模式更关注消费者的消费能力,只有消费者主动去拉取消息才会去获取消息

  1. 消息的分发策略

轮询分发: RabbitMQ 默认采用轮询分发,平均每个消费者消费相同数量的消息。若某消费者消费业务逻辑复杂处理时间过长,空闲消费者可能未被充分利用,整体应用吞吐量下降。

不公平分发: 通过设置参数 channel.basicQos(1) (信道中只允许传输一条消息),一段时间内每个消费者在同一个时间点最多处理一个消息,消费速度快的消费者可消费更多消息,能者多劳。

//表示channel上允许未ack的最大数量,一旦数量达到配置的数量,RabbitMQ将停止在通道上传递更多消息,直到某条消息被ack
//设置为0表示没有上限
channel.basicQos(int prefetchCount);

预值分发: 通过设置参数 channel.basicQos(250) 来设置消息缓冲区,一次性从信道中取大批的消息到消费端,可有效提高消费速度,间而提供吞吐量,一般建议值设置100 到 300(spring-amqp中的prefetch默认值是250)。

  1. 消息的状态流转
    ready: 可消费的消息
    ack: 确认签收后,消息将从从队列中删除
    unacked:若手动ack模式下消息未进行ack或消费异常时消息在rabbitMQ中将变成unacked状态,被标记为unacked的消息无法被立刻重新消费,需等channel重启或者服务器重启才会变成ready
    reject/nack:消费者无法处理进而拒绝消费的消息,若requeue设置为false,则消息会被丢弃(若无死信队列)

  2. 消息的过期(TTL)
    在RabbitMQ中,可以通过设置消息的过期时间来控制消息的生命周期。如下 如果在10秒内这个消息没有被消费,那么这个消息就会被从队列中移除,在过期之前消费依然可正常消费。
    通过消息发送本身的属性来设置

AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    //单位是毫秒,消息的过期时间是从RabbitMQ接收到这个消息的时刻开始计算
    .expiration("10000")
    .build();
channel.basicPublish("exchange", "routingKey", props, message.getBytes());

通过队列属性来设置

//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) 
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 10000);  //设置过期时间为10000毫秒
channel.queueDeclare("myQueue", false, false, false, args);

消息过期这一特性可用于实现延迟队列(用于存放需要在指定时间被处理的消息的队列),通过设置消息的过期时间 + 死信队列来实现。即消息在一定时间后过期,然后被发送到死信队列,消费者再从死信队列中获取消息进行处理。比如:

@Bean
public Queue paymentOrderQueue() {
		Map<String, Object> arguments = Maps.newHashMap();
		arguments.put("x-dead-letter-exchange", RabbitConstant.DEAD_EXCHANGE);
		arguments.put("x-dead-letter-routing-key",RabbitConstant.DLX_PAYMENT_HPAY_ORDER_DEAD_ROUTE_KEY);
		arguments.put("x-message-ttl", 60 * 1000);
		return QueueBuilder.durable(RabbitMqConstant.PAYMENT_HPAY_ORDER_QUEUE).withArguments(arguments).build();
	}

发起创建的订单,最终在一定时间内未完成的单进行消费死信消息进行关单处理。

  1. 死信
    死信(dead message) 是指由于某些原因无法被正常消费的mq消息。
    当消息在一个队列中变成死信之后,它可被重新被转发到另一个交换器中,这个交换机就是 DLX(Dead-Letter-Exchange) ,绑定 DLX 的队列就称之为死信队列。
    什么情况下消息会变成死信?
  • 消息被拒绝 (basicReject/basicNack) 且设置 requeue 参数为false
 //批量拒绝
 channel.basicNack(long deliveryTag, boolean multiple, boolean requeue)
 //单个消息的拒绝
 channel.basicReject(long deliveryTag, boolean requeue)
 //当requeue=false时,消息不会重新入队,消息丢失。当requeue=true会重新入队进行消费
 //requeue = true时,消息会重新放入队列头部重新消费,直至ACK成功 
 //当requeue =false 的时候,此时消息会被转发到死信队列
  • 消息过期(TTL)
  • 队列达到最大长度。
  • 消费失败达到最大重试次数
    分两种:
    RabbitMQ自身重试机制 达到最大失败次数转发到死信队列
spring:
  rabbitmq:
    listener:
      simple:
        default-requeue-rejected: false
        # 重试机制
        retry:
          enabled: true #是否开启消费者重试
          max-attempts: 3 #最大重试次数
          initial-interval: 1000 #重试间隔时间(单位毫秒)
          max-interval: 1000 #重试最大时间间隔(单位毫秒)
          multiplier: 2 #间隔时间乘子

手动ACK模式 + 消费端手动重试机制
  ①配置acknowledge-mode为manual,手动ack。
  ②消费端自身代码实现,达到失败重试次数后,拒绝消息(nack)后 转发到死信队列
在这里插入图片描述

  1. 持久化
    消息的持久化: 发送消息时将消息设置持久化,即使RabbitMQ服务down机后重启,消息也不会丢失
//实际是 deliveryMode = 2 起作用
channel.basicPublish("exchange.name", "routing.key.name", MessageProperties.PERSISTENT_TEXT_PLAIN, "message".getBytes());

队列的持久化:创建队列的时候将队列设置持久化,即使RabbitMQ服务down机后重启,队列元数据也不会丢失,但并不能保证队列中的消息不会丢失

  
//申明队列时 设置 durable为true
channel.queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments);
//或bean方式
@Bean
public Queue queueDeclare() {
   return QueueBuilder.durable("queue.name").build();
}
//或注解方式
@QueueBinding(exchange = @Exchange(value = "exchange.name", type = ExchangeTypes.FANOUT),
        value = @Queue(value = "queue.name", durable = "true")

交换机的持久化: 创建交换器的时候将交换器设置持久化,即使RabbitMQ服务down机后重启,交换器的信息也不会丢失。否则交换器将不复存在,生产者将无法发送消息到该交换机。(重要性一般)

channel.exchangeDeclare(String exchange,String type,boolean durable)throws IOException;

持久化增加了I/O操作,对于重要的业务场景(不能容忍消息丢失)应选择持久化(消息+队列),否则应选择非持久化。

将 交换机、队列、消息均设置持久化为true,是否就能保证消息百分百不丢失呢?并不能,继续往下看

  1. 信息的可靠性传输
    RabbitMq消息可能丢失情况分析:
  • Producer丢失消息(发送异常无重试)
  • Broker消息中间件自身丢失消息(未持久化、设置了持久化但未来得及刷盘就down机)
  • Consumer丢失消息(ack不当)

那么应如何保证消息不丢失?

  1. 针对 Producer丢失
    1. 事务消息机制
    将通道(Channel)设置为事务模式,所有发送到该通道的消息都将在提交事务之前被缓存,并且在提交事务后才会被投递到交换机中。如果事务提交失败,可以进行事务回滚,消息则不会被发送。
//开启事务
channel.txSelect();
try {
    // 发送消息
    channel.basicPublish("exchange_name", "routingKey_name", MessageProperties.PERSISTENT_TEXT_PLAIN, "msg".getBytes());
    // 提交事务
    channel.txCommit();
} catch (IOException e) {
    // 回滚事务
    channel.txRollback();
}

事务机制因需要在服务器端维护事务状态会带来较大的性能开销,适用于对可靠性要求极高的场景。
2. 开启发送确认机制(publisher confirm) (推荐)
生产者开启了confirm模式之后,消息成功写入rabbitmq后,rabbitmq则会回传一个ack消息;若rabbitmq未成功处理消息,则会回传nack信息,生产者可进行重试

//针对信道channel 开启生产者发布确认机制
channel.confirmSelect();
channel.basicPublish("excange_name","routingKey",null,msg.getBytes(StandardCharsets.UTF_8));
channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        //发送成功
    }
    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        //重发
    }
});

//或者 spring中 对rabbitTemplate
rabbitTemplate.convertAndSend("excange_name","routingKey","消息内容", new CorrelationData());
//设置消息确认回调方法   ConfirmCallback 是用来确认消息是否成功发送到交换机
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
    /**
     * MQ确认回调方法
     * @param correlationData 消息的唯一标识
     * @param ack 消息是否成功收到
     * @param cause 失败原因
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (!ack) {
            //重发
        }
    }
});
//设置ReturnCallback 是用来处理消息从交换机无法路由到队列时的情况
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
    @Override
    public void returnedMessage(Message message, int i, String s, String s1, String s2) {
        
    }
});
  ConfirmCallback 用于确认消息是否成功发送到交换机(交换机不存在则返回nack状态)
  ReturnCallback 用于处理消息从交换机无法路由到队列时的场景(前提 消息的mandatory属性被设置为 true)
 两者结合使用可以提高消息发送的可靠性
  亦可全局配置如下:
spring:
  rabbitmq:
    publisher-confirm-type: correlated  # 开启发送方确认机制
    publisher-returns: true   # 开启消息返回
    template:
      mandatory: true     # 消息投递失败返回客户端  

发布确认机制(异步方式),性能开销远比事务机制小的多,且能满足大多数业务场景需求,推荐使用发布确认机制而非事务机制。
在这里插入图片描述

  1. 针对 rabbitMQ消息丢失
    1. 交换机、队列、消息的持久化
    2. 镜像队列(在可靠性要求很高的场景才使用) (见第11点)

  2. 针对Consumer端消息丢失
    1. 开启手动应答模式,消费完成则正常ack, 否则 进行nack,开启配置: spring.rabbitmq.listener.simple.acknowledge-mode = manual
    2. 消费端消费失败自身重试(借助spring retry)

channel.basicAck(long deliveryTag, boolean multiple)//用于肯定确认
Channel.basicNack(long deliveryTag, boolean multiple, boolean requeue)//可用于批量否定确认
Channel.basicReject(long deliveryTag, boolean requeue)//用于单个消息否定确认
//注:  若否定确认时requeue设置为false,又未设置死信队列,消息将会被丢弃

标准使用姿势应该是怎样的?个人建议

  1. 使用可靠消息(发送失败有重试)。建立本地消息表

  2. 特别重要消息设置持久化(交换机、队列、消息持久化)。@Exchange默认持久化,队列、消息需手动设置
    在这里插入图片描述

  3. 消费注意手动ack,业务处理逻辑需有明确的应答,消费异常注意配置告警。视业务场景可对消费端消费失败进行多次本地重试

  4. 对于重要的消息可对无法正常消费的设置死信队列进行兜底处理

  5. 优先级队列
    考虑一个场景,某平台需要对大批量用心进行大促发送优惠活动,需要优先给VIP用户发送,普通用户靠后,如何实现?
    通过设置队列的优先级和指定消息的优先级来使优先级更高的队列优先被消费。

//设置队列优先级
Map<String, Object> args = new HashMap<String, Object>();  
args.put("x-max-priority" , 10);  
channel.queueDeclare("queue.priority" , true , false , false , args) ; 
//设置消息优先级
AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();  
builder.priority(5);  
AMQP.BasicProperties properties = bulder.build() ;  
channel.basicPublish("exchange.name", "rk.name" , properties, ("messages").getBytes()) ; 

使用前提: 在消费者的消费速度小于生产者的速度时实际意义更大 。若消费者的消费速度大于生产者的速度,生产者刚发送完一条消息就会被消费者消费,相当于Broker 中至多只有一条消息(无消息堆积),此时优先级实际意义不大。

  1. 镜像队列
    说到镜像队列需要先从rabbitMQ集群说起。在rabbitMQ中,普通的分布式集群模式下,各个节点间只对队列元数据进行共享,而队列中的数据(消息)只保存在某个节点上,若该节点down机,则该队列将不可用(不可消费、不能存储消息)。而镜像队列则将队列的内容复制到多个节点,以实现高可用性和冗余。当一个节点失败时,队列的内容可以从其他节点恢复(提高了消息传输的可靠性)。
    普通集群:
    在这里插入图片描述

镜像集群:
在这里插入图片描述

 镜像集群模式通过从主节点复制消息的方式使所有节点都能保留一份数据,一旦主节点崩溃,备节点就能完成替换从而继续对外提供服务(选取最早加入集群的从节点)。大量的消息复制操作会对rabbitMQ性能带来一定影响,若对负载均衡要求较高,还需结合HAProxy+KeepAlived来实现节点间负载均衡。

镜像队列设置:

// 创建一个队列
channel.queueDeclare("queue.name", true, false, false, null);
//创建一个策略,将队列设置为镜像队列
Map<String, Object> args = new HashMap<String, Object>();
args.put("ha-mode", "all");
channel.queueBind("queue.name", "exchange.name", "routingKey.name", args);
  1. 惰性队列 (Lazy Queues)
    惰性队列的特征:
  • 接收到消息后直接存入磁盘而非内存 性能和时效性受限)
  • 消费者消费消息时则从磁盘加载到内存
  • 支持更长的队列从而支持更多的消息存储,提高消息堆积的容量
    应用场景: 当消费者长时间不能消费(如 消费者宕机、下线维护而关闭)需要对消息进行堆积时 就可使用惰性队列
Map<String,Object> args = new HashMap<String,Object>();
args.put("x-queue-mode","lazy");
channel.queueDeclare("queue_name",true,false,false,args);
//或
@Bean
public Queue lazyQueue() {
  return QueueBuilder.durable("lazy.queue.name").lazy().build();
}
  1. 消息的有序性
    在保证发送有序前提下(加锁) 使用单个队列和单个消费者。但并发性差。
    使用消息的序列号。在消息中包含一个序列号,消费者按照序列号的顺序处理消息,需要消费者具有缓存和排序消息的能力。
    使用顺序队列(Ordered Queue)插件:使用RabbitMQ的插件,它可以保证即使在并发环境下,消息也会按照生产者发送的顺序被消费。但是,这个插件会影响RabbitMQ的性能。

  2. Q&A

  • 消息积压如何处理?

    1. 加消费节点,适当调大prefetchCount 值,消费端开线程池处理,提升消费速度(前提是 消费者无问题)
    2. 引入惰性队列扩大队列容积,提高消息堆积上限
    3. 若消费有问题则 修复代码
    4. 消息设置有效期,未及时处理的消息转发到死信队列进行兜底处理
  • 消息消费异常未ACK会发生什么?
    在MQ中一直是Unacked的
    在这里插入图片描述

  • BindingKey 绑定了一定会生效吗?
    并不是,它依赖于交换机类型 比如fanout 类型交换机就会无视 BindingKey,并且将消息路由到所有绑定到该交换机的队列中

  • 为什么requeue 要设置为false?
    避免在因程序异常无论如何都消费不成功时一直重复的发送消息,导致程序死循环,影响rabbitMQ的吞吐量。建议使用死信队列进行处理。

  • RabbitMQ中队列应该在生产者声明还是消费者声明更好?
    队列可以在生产者或消费者端声明,但通常最好是在消费者端声明。
    原因如下:

    1. 队列的存在是为了等待消费者来处理消息,因此消费者知道自己需要哪些队列更为合理。
    2. 如果消费者启动时队列还不存在,消费者将无法从队列中获取消息,这可能会导致错误。因此,让消费者在启动时声明它们需要的队列可以确保队列在消费者需要它们时已经存在。
  • 向不存在的 exchange 发 发送消息会发生什么?
    在这里插入图片描述

  • unacked消息达到多少会对消息消费有影响?
    一般来说,如果unacked消息的数量超过了RabbitMQ服务器的内存的50%,那么RabbitMQ会开始阻止生产者发送新的消息,直到消费者确认了一些消息,释放了一些内存。
    此外,如果unacked消息的数量超过了RabbitMQ服务器的磁盘空间的50%,那么RabbitMQ会开始删除一些旧的消息,以释放磁盘空间。因此,为了避免影响RabbitMQ的性能,你应该尽量让消费者及时确认消息,避免unacked消息的数量过多

  • requeue = true时,消息会为什么要放在队列头部?基于什么考虑设计?
    主要有两点:

  1. 保证消息的处理顺序:将消息重新放在队列的头部可以保证它在其他消息之前被再次处理,从而保证了消息的处理顺序。
  2. 快速处理失败的消息:将失败的消息放在队列的头部,可以让消费者尽快再次次处理这个消息,尝试解决处理失败的问题。如果将失败的消息放在队列的尾部,那么可能需要等待很长时间才能再次处理这个消息,这可能会导致处理失败的问题持续存在,影响系统的稳定性。
  • 事务消息原理是什么? 开启事务消息,不开消息持久化,消息什么状态才可提交(已刷盘还是进入内存即可)?
    当开启一个事务时,RabbitMQ会在内存中创建一个事务上下文,用来记录事务中的所有操作。当提交事务时,RabbitMQ会将事务上下文中的所有操作应用到实际的队列和交换器中;如果事务提交失败,那么RabbitMQ会丢弃事务上下文,所有的操作都不会被应用。开启事务消息并不意味着消息一定会被持久化,消息是否被持久化取决于你在发送消息时设置的消息属性。
    RabbitMQ的事务提交并不依赖于消息是否已经持久化或者进入内存,事务的提交是由应用代码来控制。如果在事务中的任何操作失败,或者在提交事务之前连接断开,那么可使用tx.rollback命令来回滚事务,撤销事务中的所有操作。

  • ack发布确认机制 若callBack网络异常了会发生什么?
    在RabbitMQ的发布确认机制中,生产者在发送消息后会等待RabbitMQ的确认消息。如果因为callBack网络异常,生产者无法收到确认消息,那么生产者会在等待一段时间后超时。
    超时时间是可配置的,具体的配置若可使用ConnectionFactory的setRequestedHeartbeat方法来设置心跳间隔,如果在一个心跳间隔内没有收到任何来自RabbitMQ的消息,那么生产者就会认为连接已经断开。
    当生产者检测到连接断开时,通常会尝试重新连接,并重新发送未确认的消息。这种机制可以保证消息的可靠性,避免因为网络问题导致的消息丢失,然而,这种机制也可能导致消息被重复发送。

  • 假设消息设置持久化 队列未设置持久化,down机时 消息会丢失吗?
    如果只设置了消息持久化,但没有设置队列持久化,那么在RabbitMQ服务器重启后,消息会丢失。
    在RabbitMQ中,队列和消息的持久化是两个独立的概念:

    1. 队列持久化:如果设置了队列持久化,那么在RabbitMQ服务器重启后,队列依然存在。
    2. 消息持久化:如果设置了消息持久化,那么在RabbitMQ服务器重启后,队列中的消息依然存在。
      但是,如果你只设置了消息持久化,但没有设置队列持久化,那么在RabbitMQ服务器重启后,虽然消息本身是持久化的,但是因为队列没有持久化,所以队列会丢失,队列中的消息也就随之丢失。
      所以,如果想要在RabbitMQ服务器重启后仍然保留队列和消息,你需要同时设置队列持久化和消息持久化。
  1. MQ中间件对比
    在这里插入图片描述
  2. 结束语
    寥寥数语,均为理论,实践方出真知
  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值