RocketMQ的消息顺序的面试部分回答
顺序消费保证三点:消息顺序发送;消息顺序存储;消息顺序消费
第一点,消息顺序发送,多线程发送的消息无法保证有序性,因此,需要业务方在发送时,针对同一个业务编号(如同一笔订单)的消息需要保证在一个线程内顺序发送,在上一个消息发送成功后,在进行下一个消息的发送。对应到mq中,消息发送方法就得使用同步发送,异步发送无法保证顺序性
第二点,消息顺序存储,mq的topic下会存在多个queue,要保证消息的顺序存储,同一个业务编号的消息需要被发送到一个queue中。对应到mq中,需要使用MessageQueueSelector来选择要发送的queue,即对业务编号进行hash,然后根据队列数量对hash值取余,将消息发送到一个queue中
第三点,消息顺序消费,要保证消息顺序消费,同一个queue就只能被一个消费者所消费,因此对broker中消费队列加锁是无法避免的。同一时刻,一个消费队列只能被一个消费者消费,消费者内部,也只能有一个消费线程来消费该队列。即,同一时刻,一个消费队列只能被一个消费者中的一个线程消费
RocketMQ的顺序消息分为全局有序和局部有序;
局部有序:发送同一个队列的消息有序,在发送消息时候指定队列在消费消息时候也顺序消费。
全局有序:默认topic对应多个队列。设置一个topic有一个队列时候可以实现全局有序,创建topic手动设置
保证先发的消息先存到消息队列,需使用同步方式发送。否则可能出现先发的消息后到。
例如,一个topic有两个队列,获取队列路由,比如用订单ID对消息队列数量取余,根据结果选择消费队列,同一个订单会发送到同一个队列
顺序发送:需要在配置文件中将发送方式修改为同步发送(默认为异步发送)
#设置同步发送
spring.cloud.stream.rocketmq.bindings.output.producer.sync=true
顺序消费:顺序消费有两种方式:并发消费和顺序消费,默认为并发消费,设置为顺序消费
#设置顺序消费
spring.cloud.stream.rocketmq.bindings.input.consumer.orderly=true
消息顺序消费原理:同一个队列只允许消费者中一个线程消费。消费者中有个消费线程池,多个线程会同时消费。在顺序消费下消费线程到broker会申请独占锁,获得锁请求才消费,消费成功后会向broker提消费进度,更新位点信息,避免下次重复消费
顺序消息的局限性:
1:发送顺序消息无法利用集群的FailOver特性;
2:队列热点问题,个别队列因为hash不均导致消息过多,消费速度跟不上,产生消息堆积
3:遇到消息消费失败,无法跳过失败消息,当前队列暂停