RocketMQ问题总结
消息队列的作用:异步、解耦、削峰填谷。
1、处理失败消息重发问题
场景:比如活动系统、积分系统同时监听用户下单消息,但是此时可能由于网络抖动或是代码bug导致积分系统处理失败,所以积分系统就会要求发送端重发消息。如何保证消息重复不会影响业务呢?
思考:这个问题其实就是保证接口的幂等性,那如何保证接口幂等性,根据场景可以分为强校验和弱校验。
(1)强校验:跟钱相关的,比如订单这种,最好创建记录表,每次消息过来都要拿着订单号+业务场景这样的唯一标识去流水表查,看看有没有这条记录,有就直接return,没有就执行后面的逻辑。
(2) 弱校验:比如发短信,把这个id+场景唯一标识作为Redis的key,放到缓存里面失效时间看场景,一定时间内的这个消息就去Redis判断。
rocketMQ如何实现消息重发???
2、消息顺序消费问题
场景:主要用在数据库binlog,同步数据的增删改操作。
思考:顺序发送:一个topic对应多个队列(一般是4个),RocketMQ提供了MessageQueueSelector选择机制(队列的路由策略),比如订单消息,可以对订单ID进行hash取模,发送时使用同步发送,让同一个订单发的操作有序送到相同的队列中。
顺序消费:如果是单线程,同一个订单的操作在同一个队列中,根据FIFO就可以保证顺序消费,如果是多线程,可以在循环处理消息时将同一个订单的所有操作封装在一起在扔到线程池处理。
代码实现:RocketMQ提供了并发和顺序的消息监听方式,并发监听MessageListenerConcurrently,也就是基于多个线程并行来消费消息,无法保证消息消费的顺序。顺序监听MessageListenerOrderly,如下:
consumer.subscribe("store_topic_test","*"); consumer.registerMessageListener((MessageListenerOrderly) (list, consumeOrderlyContext) -> { list.stream().forEach(messageExt -> System.out.println(new String(messageExt.getBody()))); return ConsumeOrderlyStatus.SUCCESS; } );
顺序消费会带来一些问题:
(1)遇到消息处理失败,无法跳过,当前队列消费暂停
(2)降低消息处理的性能
3、多环境隔离、可靠性消息发送、任意延时消息实现方案
(1)多环境隔离使用RocketMQ自带的队列路由策略
(2)可靠性发送和任意延时消息使用嵌入式k-v数据库RocksDB
文章:https://juejin.im/post/5e8fac605188256bdf72b24d
4、如何保证不丢消息
分为三个阶段:
(1)生产阶段:有同步发送和异步发送,判断Broker返回的状态,建立失败补偿机制
(2)Broker存储阶段:消息先存在内存中,然后再刷盘,分为同步刷盘和异步刷盘,可以使用同步刷盘,但是会影响性能。
(3)消费阶段:控制消息返回状态。
文章:https://zhuanlan.zhihu.com/p/116402583
5、消费消息的两种方式 pull 和 push
push方式里,consumer把轮询过程封装了,并注册MessageListener监听器,取到消息后,唤醒MessageListener的consumeMessage()来消费,对用户而言,感觉消息是被推送过来的。push方式其实也是拉取消息,不过封装好了轮询过程。该方式实时性高,用起来方便。
文章:https://blog.csdn.net/weixin_45784983/article/details/134309404
pull方式里,取消息的过程需要用户自己写,首先通过打算消费的Topic拿到MessageQueue的集合,遍历MessageQueue集合,然后针对每个MessageQueue批量取消息,一次取完后,记录该队列下一次要取的开始offset,直到取完了,再换另一个MessageQueue。该方式控制权在消费端,好控制,但是拉取的时间间隔不好定义。