rocketMQ(六) rocketMQ顺序投递并消费-附demo

目标:电商订单生产并消费

1.准备知识一览

1.1 MessageQueueSelector了解

生产消息使用MessageQueueSelector投递到Topic下指定的queue

producer.send(message, new MessageQueueSelector(){
			select(List<MessageQueue> mqs, Message msg, Object arg){
				Integer queueNum = (Integer)arg;
				return mqs.get(queueNum);
			}
	},0)
  1. 应用场景:顺序消息,分摊负载

  2. 默认topic下的queue数量是4,可以配置

  3. 支持同步,异步发送指定的MessageQueue(不建议使用异步)。

  4. 选择的queue数量必须小于配置的,否则会出错

1.2 顺序消息介绍

1.2.1什么是顺序消息:消息的生产和消费顺序一致

  1. 全局顺序:topic下面全部消息都要有序(少用)

    (1)性能要求不高,所有的消息严格按照 FIFO 原则进行消息发布和消费的场景,并行度成为消息系统的瓶颈, 吞吐量不够.

    (2)在证券处理中,以人民币兑换美元为例子,在价格相同的情况下,先出价者优先处理,则可以通过全局顺序的方式按照 FIFO 的方式进行发布和消费

  2. 局部顺序:只要保证一组消息被顺序消费即可
    (1)性能要求高

    (2)电商的订单创建,同一个订单相关的创建订单消息、订单支付消息、订单退款消息、订单物流消息、订单交易成功消息 都会按照先后顺序来发布和消费
    (阿里巴巴集团内部电商系统均使用局部顺序消息,既保证业务的顺序,同时又能保证业务的高性能)

1.2.2 顺序发布:对于指定的一个 Topic,客户端将按照一定的先后顺序发送消息

1.2.3 顺序消费:对于指定的一个 Topic,按照一定的先后顺序接收消息,即先发送的消息一定会先被客户端接收到。

注意: 1.顺序消息暂不支持广播模式 2.顺序消息不支持异步发送方式,否则将无法严格保证顺序

1.3 生产端发送消息有序

生产端保证发送消息有序,且发送到同一个Topic的同个queue里面,RocketMQ的确是能保证FIFO的,以订单orderId为例

Message msg = new Message("orderServer", "", order.getOrderId(),
                    order.toString().getBytes());
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
       @Override
       public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
           // arg 即从外部传递进来的 order.getOrderId()
           // 对orderId 进行取余 计算, 保证同一订单的信息 发送到同一个 topic 下的同一个queue 里面,保证生产有序
           Integer id = (Integer) arg;
           int index = id % mqs.size();
           return mqs.get(index);
      }
}, order.getOrderId());

1.4 消费端消费有序

MessageListenerConcurrently:并发消费
MessageListenerOrderly:

  1. Consumer会平均分配queue的数量

  2. 并不是简单禁止并发处理,而是为每个Consumer Quene加个锁,消费每个
    消息前,需要获得这个消息所在的Queue的锁,这样同个时间,同个Queue的
    消息不被并发消费,但是不同Queue的消息可以并发处理
    代码


/**
 * 顺序消息消费
 */
public class Consumer {
 
    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("orderServer");
        consumer.setNamesrvAddr("192.168.159.128:9876;192.168.159.129:9876");
        /**
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
         * 如果非第一次启动,那么按照上次消费的位置继续消费
         */
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
 
        consumer.subscribe("orderServer", "TagA || TagC || TagD");
 
        consumer.registerMessageListener(new MessageListenerOrderly() {
 
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                MessageExt message = msgs.get(0);
                try {
                    //模拟业务逻辑处理中...
                    return ConsumeOrderlyStatus.SUCCESS;
                } catch (Exception e) {
                    e.printStackTrace();
                    //补偿机制
                    return ConsumeOrderlyStatus.SYSPEND_CURRENT_QUEUE_A_MOMENT;
                }
                
            }
        });
 
        consumer.start();
        System.out.println("Consumer Started.");
    }
}

posted on 2020-05-05 11:07  顾~小诺  阅读(629)  评论(0编辑  收藏  举报

导航