背景
- 在实际的业务需求开发中,我们更多的只是使用了Rocket Mq里消费者的消费功能,只需要重写消息监听器里的onMessage方法,对消息内容进行业务逻辑的处理。
- 但是我们很少去关注当前消费者是怎么去队列里拉取消息的?一次性拉取多少消息?(单条拉取?还是批量拉取?),拉取到消息之后会进行消费,那么如果消费成功,下一次拉取是什么时候?从队列哪个位置开始拉取?如果消费失败呢?会不会重新拉取失败的消息再次消费?还是直接丢弃?................
问题
- 消费者拉取队列消息时,是单条拉取还是批量拉取?
- 批量消费时,如果消息全部消费成功,怎么保存消费进度(offset)?
- 批量消费时,如果存在部分消息消费失败,怎么保存消费进度?
- 批量消费时,消费到一半,因为迭代发布导致机器重启,消费进度没有及时提交,会存在什么问题?
浅析MQ ACK机制
消费方式
- 并发消费:一个队列中的消息可同时被消费者的多个线程并发消费
- 顺序消费:一个队列中的消息同一时间只能被一个消费者的一个线程消费,通过加锁达到顺序消费的效果
消费模式
-
广播模式:消费者组里的每个消费者都能消费Topic下所有队列的消息(全量数据)
-
集群模式:消费者组里的每个消费者只能消费部分队列的消息(部分数据)
-
offset:表示消费者组在当前消息队列的消费进度
ACK机制
- Rocket Mq是通过offset来标记一个消费者组在队列上的消费进度,消费成功之后都会返回一个ack消息告诉broker去更新offset,但是RocketMQ并不是每消费一条消息就做一次ack,而是消费完批量消息后只做一次ack
- 所以ACK机制是为了准确的告知Broker批量消费成功的信息并且更新消费进度
- 那批量消费时具体是如何更新消费进度?
- 每一条消息消费成功后,会按照当前消息最小的offset来更新本地的消费进度
- 由5秒的定时任务将offset提交到Broker
- 批量更新的好处在于不用每次消费成功都提交offset,提升了消费效率,但也会随着产生一下两个问题
- 比如101消息由于一些原因没有消费完成,而 102 - 110 消息都消费完了,此时broker的消费进度仍然是101,如果此时该消费者宕机或者实例被kill,当前queue会通过负载均衡策略会重新被分配给其它的消费者,这个时候从broker拉取消息时,是从101开始拉取消费,但是实际102-110这9条消息已经消费完成,造成这9条消息重复消费
- 既然是按批量来更新消费进度,但是那些虽然消费完成但是实际是失败的消息(主动返回RECONSUME_LATER和抛出异常)的消息是如何处理?
- consumer在处理消费失败的消息时,会单独把该消息的信息通过rpc调用通知给broker,那broker会把该消息做重新的投递,从而做到了消息的重置机制
总结:ACK机制
-
- 优点:防止消息丢失
- 缺点:会造成消息重复消费(使用方做幂等)
问题解答
- 消费者拉取队列消息时,是单条拉取还是批量拉取?
- 批量
- 批量消费时,如果消息全部消费成功,怎么保存消费进度(offset)?是每条消息消费成功之后都会提交一次offset?还是批量消息全部消费成功之后只提交一次offset?
- 每一条消息消费成功会按照当前消息最小的offset来更新本地的消费进度
- 由5秒的定时任务将offset提交到Broker
- 批量消费时,如果存在消息消费失败,怎么保存消费进度?
- 消费失败的消息也算消费成功(所以按照消费成功的逻辑保存消费进度),只不过失败的消息会重新投递给Broker的 RETRY Topic下(并非原来Topic)
- 批量消费时,消费到一半,因为迭代发布导致机器重启,导致消费进度没有提交,会存在什么问题
- 会导致当前队列被负载均衡到其他消费者消费时,发生重复消费问题,所以一般都我们业务方保证消费幂等