1. RocketMQ如何防止消息丢失
1.1 RocketMQ丢消息的场景
- 生产者向RocketMQ发送消息时
- RocketMQ主节点向从节点同步消息时
- 消费者向RocketMQ拉取消息消费时
1.2 生产者端使用事务消息机制防止消息丢失
1.2.1 half消息的作用
在本地事务执行之前发送给RocketMQ,且对下游的积分服务是不可见的。所以,half消息的作用就是确认RocketMQ服务的可用性。
1.2.2 half消息写入失败了怎么处理
一般来说,half消息写入失败,即认为RocketMQ服务异常,即消息无法正常推送给下游服务。建议在half消息写入失败时,给正在处理的订单写上一个状态标记,等待RocketMQ服务正常后即可进行补偿操作。
1.2.3 下单操作写数据库失败怎么处理
假设执行本地事务下单时,DB异常,所以此时的下单写数据库就会失败。可以考虑把此时的订单信息存入其他地方,比如Redis,并且给RocketMQ返回一个未知(UNKNOWN)的状态。由于事务消息的机制,RocketMQ会定时回查该条订单的事务的状态。
1.2.4 订单超时怎么设置
- 定时任务。定时扫描订单表,比对未支付的订单的下单时间。
- RocketMQ的延迟消息机制。向RocketMQ发送一个1分钟的延迟消息,拿到这个消息后检查订单的支付状态,已经支付则往下游发送,未支付则再发送一个1分钟的延迟消息,知道在第X个消息时关闭该订单。
- 事务消息。在下单时,给Broker返回一个UNKNOWN的事务未知状态,而在状态回查的方法中去查询订单的支付状态(只需配置回查次数,默认15次,和事务回查间隔时
间messageDelayLevel)。
1.3 RocketMQ端使用同步刷盘和Dledger主从架构防止消息丢失
1.3.1 同步刷盘和异步刷盘
异步刷盘:
在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入 。
同步刷盘:
在返回应用写成功状态前,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,给应用返回消息写成功的状态。
1.3.2 Dledger的文件同步
Dledger搭建的RocketMQ集群,会通过两阶段提交的方式保证文件在主从之间成功同步。
简单来说,数据同步会通过两个阶段,一个是uncommitted阶段,一个是commited阶段:
- Leader Broker上的Dledger收到一条数据后,会标记为uncommitted状态
- 通过自己的DledgerServer组件把这个uncommitted数据发给Follower Broker的DledgerServer组件
- Follower Broker的DledgerServer收到uncommitted消息之后,必须返回一个ack给Leader Broker的Dledger
- 如果Leader Broker收到超过半数的Follower Broker返回的ack之后,就会把消息标记为committed状态
- Leader Broker上的DledgerServer就会发送committed消息给Follower Broker上的DledgerServer,让他们把消息也标记为committed状态
1.4 消费者端使用同步消费机制
消费者从 broker 拉取消息,然后执行相应的业务逻辑。一旦执行成功,将会返回ConsumeConcurrentlyStatus.CONSUME_SUCCESS 状态给 Broker。
如果 Broker 未收到消费确认响应或收到其他状态,消费者下次还会再次拉取到该条消息,进行重试。
2.RocketMQ如何保证消息有序
- 全局有序:整个MQ系统的所有消息严格按照队列先入先出顺序进行消费
- 局部有序:只保证一部分关键消息的消费顺序
一般来说,消息有序指的是局部有序。
对于局部有序,只需要将有序的一组消息都存入同一个MessageQueue里,这样MessageQueue的FIFO(顺序消息)设计天生就可以保证这一组消息的有序。即可以在发送者发送消息时指定一个MessageSelector对象,让这个对象来决定消息发入哪一MessageQueue,这样就可以保证一组有序的消息能够发到同一个MessageQueue里。
比如,对订单号取一个特定的模再发往selector中,selector就可以保证同一个模的都会投递到同一条MessageQueue。
即,相同订单号的—>有相同的模—>有相同的queue。
消费端只需保证顺序消费即可以实现局部有序。
如果消费端是使用MessageListenerOrderly则已经默认实现了顺序消费,如果是使用了MessageListenerConcurrently则只需把线程池改成单线程模式。
3. RocketMQ如何快速处理积压消息
3.1 该Topic下的MessageQueue配置数量比较多
横向扩容。
增加Consumer的服务节点数量来加快消息的消费,等积压消息消费完了,再恢复成正常情况。最极限的情况是把Consumer的节点个数设置成跟MessageQueue的个数相同。但是如果此时再继续增加Consumer的服务节点就没有用了。
3.2 该Topic下的MessageQueue配置数量比较少
创建一个新的Topic,配置足够多的MessageQueue。然后把所有消费者节点的目标Topic转向新的Topic,并紧急上线一组新的消费者,只负责消费旧Topic中的消息,并转储到新的Topic中,这个速度是可以很快的。然后在新的Topic上,就可以通过增加消费者个数来提高消费速度了。之后再根据情况恢复成正常情况。