2 顺序消息样例

消息有序指的是可以按照消息的发送顺序来消费(FIFO)。RocketMQ可以严格的保证消息有序,可以分为分区有序或者全局有序。

顺序消费的原理解析,在默认的情况下消息发送会采取Round Robin轮询方式把消息发送到不同的queue(分区队列);而消费消息的时候从多个queue上拉取消息,这种情况发送和消费是不能保证顺序。但是如果控制发送的顺序消息只依次发送到同一个queue中,消费的时候只从这个queue上依次拉取,则就保证了顺序。当发送和消费参与的queue只有一个,则是全局有序;如果多个queue参与,则为分区有序,即相对每个queue,消息都是有序的。

下面用订单进行分区有序的示例。一个订单的顺序流程是:创建、付款、推送、完成。订单号相同的消息会被先后发送到同一个队列中,消费时,同一个OrderId获取到的肯定是同一个队列。

2.1 顺序消息生产

  1. package org.apache.rocketmq.example.order2;
  2. import org.apache.rocketmq.client.producer.DefaultMQProducer;
  3. import org.apache.rocketmq.client.producer.MessageQueueSelector;
  4. import org.apache.rocketmq.client.producer.SendResult;
  5. import org.apache.rocketmq.common.message.Message;
  6. import org.apache.rocketmq.common.message.MessageQueue;
  7. import java.text.SimpleDateFormat;
  8. import java.util.ArrayList;
  9. import java.util.Date;
  10. import java.util.List;
  11. /**
  12. * Producer,发送顺序消息
  13. */
  14. public class Producer {
  15. public static void main(String[] args) throws Exception {
  16. DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
  17. producer.setNamesrvAddr("127.0.0.1:9876");
  18. producer.start();
  19. String[] tags = new String[]{"TagA", "TagC", "TagD"};
  20. // 订单列表
  21. List<OrderStep> orderList = new Producer().buildOrders();
  22. Date date = new Date();
  23. SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  24. String dateStr = sdf.format(date);
  25. for (int i = 0; i < 10; i++) {
  26. // 加个时间前缀
  27. String body = dateStr + " Hello RocketMQ " + orderList.get(i);
  28. Message msg = new Message("TopicTest", tags[i % tags.length], "KEY" + i, body.getBytes());
  29. SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
  30. @Override
  31. public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
  32. Long id = (Long) arg; //根据订单id选择发送queue
  33. long index = id % mqs.size();
  34. return mqs.get((int) index);
  35. }
  36. }, orderList.get(i).getOrderId());//订单id
  37. System.out.println(String.format("SendResult status:%s, queueId:%d, body:%s",
  38. sendResult.getSendStatus(),
  39. sendResult.getMessageQueue().getQueueId(),
  40. body));
  41. }
  42. producer.shutdown();
  43. }
  44. /**
  45. * 订单的步骤
  46. */
  47. private static class OrderStep {
  48. private long orderId;
  49. private String desc;
  50. public long getOrderId() {
  51. return orderId;
  52. }
  53. public void setOrderId(long orderId) {
  54. this.orderId = orderId;
  55. }
  56. public String getDesc() {
  57. return desc;
  58. }
  59. public void setDesc(String desc) {
  60. this.desc = desc;
  61. }
  62. @Override
  63. public String toString() {
  64. return "OrderStep{" +
  65. "orderId=" + orderId +
  66. ", desc='" + desc + '\'' +
  67. '}';
  68. }
  69. }
  70. /**
  71. * 生成模拟订单数据
  72. */
  73. private List<OrderStep> buildOrders() {
  74. List<OrderStep> orderList = new ArrayList<OrderStep>();
  75. OrderStep orderDemo = new OrderStep();
  76. orderDemo.setOrderId(15103111039L);
  77. orderDemo.setDesc("创建");
  78. orderList.add(orderDemo);
  79. orderDemo = new OrderStep();
  80. orderDemo.setOrderId(15103111065L);
  81. orderDemo.setDesc("创建");
  82. orderList.add(orderDemo);
  83. orderDemo = new OrderStep();
  84. orderDemo.setOrderId(15103111039L);
  85. orderDemo.setDesc("付款");
  86. orderList.add(orderDemo);
  87. orderDemo = new OrderStep();
  88. orderDemo.setOrderId(15103117235L);
  89. orderDemo.setDesc("创建");
  90. orderList.add(orderDemo);
  91. orderDemo = new OrderStep();
  92. orderDemo.setOrderId(15103111065L);
  93. orderDemo.setDesc("付款");
  94. orderList.add(orderDemo);
  95. orderDemo = new OrderStep();
  96. orderDemo.setOrderId(15103117235L);
  97. orderDemo.setDesc("付款");
  98. orderList.add(orderDemo);
  99. orderDemo = new OrderStep();
  100. orderDemo.setOrderId(15103111065L);
  101. orderDemo.setDesc("完成");
  102. orderList.add(orderDemo);
  103. orderDemo = new OrderStep();
  104. orderDemo.setOrderId(15103111039L);
  105. orderDemo.setDesc("推送");
  106. orderList.add(orderDemo);
  107. orderDemo = new OrderStep();
  108. orderDemo.setOrderId(15103117235L);
  109. orderDemo.setDesc("完成");
  110. orderList.add(orderDemo);
  111. orderDemo = new OrderStep();
  112. orderDemo.setOrderId(15103111039L);
  113. orderDemo.setDesc("完成");
  114. orderList.add(orderDemo);
  115. return orderList;
  116. }
  117. }

2.2 顺序消费消息

  1. import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
  2. import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
  3. import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
  4. import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
  5. import org.apache.rocketmq.common.message.MessageExt;
  6. import java.util.List;
  7. package org.apache.rocketmq.example.order2;
  8. import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
  9. import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
  10. import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
  11. import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
  12. import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
  13. import org.apache.rocketmq.common.message.MessageExt;
  14. import java.util.List;
  15. import java.util.Random;
  16. import java.util.concurrent.TimeUnit;
  17. /**
  18. * 顺序消息消费,带事务方式(应用可控制Offset什么时候提交)
  19. */
  20. public class ConsumerInOrder {
  21. public static void main(String[] args) throws Exception {
  22. DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_3");
  23. consumer.setNamesrvAddr("127.0.0.1:9876");
  24. /**
  25. * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
  26. * 如果非第一次启动,那么按照上次消费的位置继续消费
  27. */
  28. consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
  29. consumer.subscribe("TopicTest", "TagA || TagC || TagD");
  30. consumer.registerMessageListener(new MessageListenerOrderly() {
  31. Random random = new Random();
  32. @Override
  33. public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
  34. context.setAutoCommit(true);
  35. for (MessageExt msg : msgs) {
  36. // 可以看到每个queue有唯一的consume线程来消费, 订单对每个queue(分区)有序
  37. System.out.println("consumeThread=" + Thread.currentThread().getName() + "queueId=" + msg.getQueueId() + ", content:" + new String(msg.getBody()));
  38. }
  39. try {
  40. //模拟业务逻辑处理中...
  41. TimeUnit.SECONDS.sleep(random.nextInt(10));
  42. } catch (Exception e) {
  43. e.printStackTrace();
  44. }
  45. return ConsumeOrderlyStatus.SUCCESS;
  46. }
  47. });
  48. consumer.start();
  49. System.out.println("Consumer Started.");
  50. }
  51. }