1、为什么会产生这个问题?
默认MQ没有实现顺序消费,因为RocketMQ1个topic中有四个队列,且消费是多线程进行消费的。即使队列能够保证先进先出,但是由于多线程CPU执行的随机性,我们无法保证消费的顺序,从而导致了消费的乱序。
2、解决方法
RocketMQ提供了解决办法,我们只需要将需要顺序执行的消息通过取模运算的方式放到同一个队列中。在生产者和消费者的代码中选择合适的方法(代码如下)。
3、代码实现
//生产者代码
public class Producer {
public static void main(String[] args) throws Exception{
//定义一个生产对象
DefaultMQProducer producer = new DefaultMQProducer("orderlyProducerGroup");
//连接nameServer
producer.setNamesrvAddr("10.0.0.129:9876");
//启动生产者
producer.start();
//设置消息发送的目的地Topic
String topic = "orderTopic";
List<OrderStep> orderSteps = OrderUtil.buildOrders();
MessageQueueSelector selector = new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
System.out.println("队列的个数是"+list.size());
Long orderId = (Long) o;
//根据订单号选取队列,订单号相同的队列相同,返回list中的队列
int index = (int) (orderId%list.size());//这样返回的值就是0 1 2 3
return list.get(index);
}
};
for (OrderStep step : orderSteps) {
Message msg = new Message(topic, step.toString().getBytes(Charset.defaultCharset()));
//指定消息选择器 (消息,队列选择器,顺序消费的参数【这里是订单号就是上面消费选择器里的Object o参数】)
producer.send(msg,selector,step.getOrderId());
}
System.out.println("发送完毕");
producer.shutdown();
}
}
//消费者代码
public class Consumer {
public static void main(String[] args) throws Exception {
//定义同一个消息消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderlyConsumerGroup");
//设置NameServer地址
consumer.setNamesrvAddr("10.0.0.129:9876");
//设置订阅的主题
consumer.subscribe("orderTopic","*");
//从什么地方开始消费 (为了实现顺序消费,从头开始消费)
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//设置消息的监听器
consumer.setMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt msg : list) {
System.out.println("当前线程:"+Thread.currentThread()+",队列ID:"+msg.getQueueId()+",消息内容"+new String(msg.getBody(),Charset.defaultCharset()));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
//启动消费者
consumer.start();
}
}