Rocket MQ 实现消息的顺序生产和消费
1. 生产者 实现消息顺序,必须让一个订单都进入一个MessageQueue中
这里有两种方式: 1. 取模运算 2. hash 运算
// 调用生产者的send方法发送消息,并接收发送结果
SendResult sendResult = producer.send(
// 要发送的消息对象
message,
// 消息队列选择器,用于指定消息发送到哪个队列
new MessageQueueSelector() {
@Override
// 实现select方法来选择消息队列
public MessageQueue select(
// 可用的消息队列列表
List<MessageQueue> mqs,
// 当前要发送的消息
Message msg,
// 用于选择队列的参数
Object arg) {
// 将传入的参数转换为Long类型的订单id
Long orderId = (Long) arg;
// 根据订单id对消息队列数量取模,得到索引值
long index = orderId % mqs.size();
// 根据索引从消息队列列表中获取一个消息队列并返回
return mqs.get((int) index);
}
},
// 传入订单id作为选择队列的参数
orderid
);
2. 消费者保证按照顺序来获取一个MessageQueue中的消息
// 为消费者注册一个有序消息监听器
consumer.registerMessageListener(
new MessageListenerOrderly() {
@Override
// 实现consumeMessage方法来处理有序消息
public ConsumeOrderlyStatus consumeMessage(
// 待处理的有序消息列表
List<MessageExt> msgs,
// 有序消息消费上下文
ConsumeOrderlyContext context) {
// 设置自动提交消息确认,即消息处理成功后自动标记为已消费
context.setAutoCommit(true);
try {
// 遍历待处理的消息列表
for (MessageExt msg : msgs) {
// 在这里编写对每条有序消息的具体处理逻辑
}
// 表示所有消息都处理成功,返回成功状态
return ConsumeOrderlyStatus.SUCCESS;
} catch(Exception e) {
// 如果在消息处理过程中发生异常
// 返回暂停当前队列处理的状态,稍后再继续处理这批消息
// return SUSPEND_CURRENT_QUEUE_A_MOVEMENT;
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
}
}
);
使用的是MessageListenerOrderly这个东西,他里面有Orderly这个名称
也就是说,Consumer会对每一个ConsumeQueue,都仅仅用一个线程来处理其中的消息。交给Consumer中的多个线程来处理的话,那还是会有消息乱序的问题。
对于有序消息的方案中,如果你遇到消息处理失败的场景,就必须返回SUSPEND_CURRENT_QUEUE_A_MOMENT这个状态,意思是先等一会儿,一会儿再继续处理这批消息,而不能把这批消息放入重试队列去,然后直接处理下一批消息,依然会存在消息错乱的问题。