消息队列之消息可靠消费

RabbitMQ消息生命周期

(1)生产者将消息发送给交换机

(2)交换机将消息放到绑定的队列里;

(3)消费者从队列中获取消息,并进行业务的处理。

整个链路可能出现的问题

1、生产者发送消息过程出现问题

2、RabbitMQ Broker存储消息失败

3、消费者消费消息过程出现问题

要保证消息可靠消费,需要

          (1)确保生产者发送的消息被rabbitmq接收了;

          (2)确保队列中的消息能够持久化;

          (3)确保消费者已经把消息处理完了。

确保消息到达消息中间件
        

        为了实现这一点,有两种途径,一是rabbitmq 提供的事务机制,二是发送方确认机制(publisher confirm)。

1、事务机制

RabbitMQ客户端中与事务机制相关的方法有三个

channel.txSelect:用于将当前的信道设置成事务模式,
channel.txCommit:用于提交事务,
channel.txRollback:用于事务回滚。
(1)生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,

(2)如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;

(3)如果收到了消息,那么可以提交事务channel.txCommit。

业务实现如下

// 开启事务
channel.txSelect
try {
    // 这里发送消息
} catch (Exception e) {
    channel.txRollback
 
    // 这里再次重发这条消息、或者落库补偿机制处理
}
 
// 提交事务
channel.txCommit

事务机制将使得吞吐量降低!

2、confirm-ack机制

  1. 方式一:channel.waitForConfirms()普通发送方确认模式;

  2. 方式二:channel.waitForConfirmsOrDie()批量确认模式;

  3. 方式三:channel.addConfirmListener()异步监听发送方确认模式;

(1)在生产者那里设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。

(2)如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。

        事务机制和 cnofirm 机制:最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm 机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息 RabbitMQ 接收了之后会异步回调你的一个接口通知你这个消息接收到了。

RabbitMQ Broker 消息持久化


  确保持久化需要两个步骤:

(1)创建 queue 的时候将其设置为持久化
  这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的。

(2)第二个是发送消息的时候将消息的 deliveryMode 设置为 2
  就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。

 必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。

【注意】

哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。

所以,持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack ,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 ack,你也是可以自己重发的。

确保消费者消息消费成功


为了保证消息从队列可靠地达到消费者,RabbitMQ提供了消息确认机制(message acknowledgement)。

消费者在订阅队列时,可以指定autoAck参数,

(1)autoAck等于false时,RabbitMQ会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)。

(2)当autoAck等于true时,RabbitMQ会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正的消费到了这些消息。

(3)RabbitMQ不会为未确认的消息设置过期时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开,这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很久很久。

如果消息消费失败,也可以调用Basic.Reject或者Basic.Nack来拒绝当前消息而不是确认,如果只是简单的拒绝那么消息会丢失,需要将相应的requeue参数设置为true,那么RabbitMQ会重新将这条消息存入队列,以便可以发送给下一个订阅的消费者。如果requeue参数设置为false的话,RabbitMQ立即会把消息从队列中移除,而不会把它发送给新的消费者。
 

其他:

requeue的消息是存入队列头部的,即可以快速的又被发送给消费,如果此时消费者又不能正确的消费而又requeue的话就会进入一个无尽的循环之中。对于这种情况,笔者的建议是在出现无法正确消费的消息时不要采用requeue的方式来确保消息可靠性,而是重新投递到新的队列中,比如设定的死信队列中,以此可以避免前面所说的死循环而又可以确保相应的消息不丢失。对于死信队列中的消息可以用另外的方式来消费分析,以便找出问题的根本。
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值