Skip to the content.

首页

消息队列


使用场景


消息丢失

可能发生在生产者端、MQ端、消费者端,总结来说是生产者保证消息最少成功发送一次,MQ保证消息落盘后才返回成功,MQ保证消费者至少成功消费一次。

生产者丢消息

应该选择有回调方式,即不选择单向发送,同步发送不会出现丢消息问题(但有网络可靠性问题),在有本地事务的场景下应该使用事务消息(时效性严格场景)或本地消息表方案

本地消息表方案

  1. 保存消息至本地消息表,与本地事务一起提交;
  2. 发送异步消息,注册监听器;
  3. MQ回调通知发送结果,更新本地消息表消息状态;
  4. 定时检查消息状态,一段时间后如仍未收到MQ回调则执行重试

有本地事务的场景下同步发送的问题

网络可靠性问题

网络可靠性问题是容易被忽视的一环,网络超时可能导致生产者可能无法接受到来自MQ端的响应。则消息生产者会认为消息发送失败,但实际上MQ已经持久化了消息,如果因此回滚了本地事务则会造成严重影响。

MQ丢消息

MQ接受到消息后先保存到内存,还没来得及保存到磁盘或同步给从节点即宕机,MQ可以保证消息不丢失,但是会损失性能。

消费者丢消息

消费者消费消息不能自动ack,否则消费者接受到消息后宕机,而MQ自动认为消费成功。


消费模型

消费者数量要小于等于topic中队列(或分区)数量,集群模式下消费模型分为顺序消费和并发消费,并行消费允许多个消费者线程从同一个队列中拉取消息,顺序消费则是只允许一个。

重复消费

在所有消息系统中消费消息有三种模式:at-most-once(最多一次)、at-least-once(最少一次)和 exactly-only-once(精确仅一次),RocketMQ和Kafka都是保证at-lease-once,如果要保证消息不丢失则会由重复消费的可能,需要自行在业务层面保证消费的幂等性。


顺序消息

顺序消息分为局部有序和全局有序,前提是保证有序的发送,同时消费者端一般也需要顺序消费

记录消费顺序 + 本地消息表

生产者发送消息时记录消费顺序,消费者并发消费消息,持久化消息到本地消息表后提交ack,之后按照消息的消费顺序进行异步消费即可。


消息积压

如果由于程序问题导致积压,使得topic内其他消息也无法被消费,则需要新增一个临时消费者,消费掉堆积的消息,转发到新的topic或保存到数据库等。


RocketMQ

消息保存流程

MQ接收到消息,首先使用顺序IO的方式保存到commitlog文件,之后执行重投递,投递到ConsumeQueue文件和IndexFile文件。

事务消息

消息发送成功后才提交本地事务的问题是无法随本地事务一起回滚,所以需要事务消息,使用本地消息表解决方案也可以实现事务消息

  1. 生产者向MQ发送‘半消息’,此时消息对消费者不可见;
  2. ‘半消息’发送成功后MQ回调生产者执行本地事务,本地事务执行完成后发送提交或回滚请求;
  3. 由于MQ和生成者都有故障的可能,所以MQ需要能向生成者确认本地事务的执行结果,一般将本地事务执行结果保存到数据库或分布式缓存中

延迟消息

不支持任意时间的延时,只支持几个固定的延时等级,所有的延迟消息由producer发出之后,都会存放到同一个topic下(SCHEDULE_TOPIC_XXXX),不同的延迟级别会对应不同的队列序号,每个队列对应一个定时线程,定时处理到期的消息并重新投递到真正指定的topic下,此时消息才对于consumer端可见。

消费模式

pull & push

RocketMQ并没有真正实现Push模式,而是对Pull模式进行一层包装,通过Pull不断轮询Broker获取消息,当不存在新消息时,Broker会挂起请求,直到有新消息产生,取消挂起,返回新消息。


Kafka

由多个broker组成,每个broker是一个节点;每个topic可以划分为多个 partition,每个partition可以存在于不同的broker上;每个partition又可以有多个replica,选举出一个leader,其他作为follower。

吞吐量高的原因

再平衡

消费者集群中新增或删除消费者、topic组下新增topic、topic的分区调整,会触发再平衡,即重新分配分区到消费者。

ZooKeeper作用

使用ZooKeeper作为分布式协调,2.8版本开始会支持使用Raft协议的Quorum。