Loading

RocketMQ有序消息

有序消息的基本概念

image
image
image
image
image
image

package com.study.rocketmq.a153_order;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 有序消息
 */
public class OrderedProducer {
    public static final String NAME_SERVER_ADDR = "192.168.100.242:9876";

    public static void main(String[] args) throws MQClientException, InterruptedException, RemotingException, MQBrokerException, UnsupportedEncodingException {
        // 1:创建生产者对象,并指定组名
        DefaultMQProducer producer = new DefaultMQProducer("GROUP_TEST");

        // 2:指定NameServer地址
        producer.setNamesrvAddr(NAME_SERVER_ADDR);

        // 3:启动生产者
        producer.start();
        producer.setRetryTimesWhenSendAsyncFailed(0); // 设置异步发送失败重试次数,默认为2

        // 4:定义消息队列选择器
        MessageQueueSelector messageQueueSelector = new MessageQueueSelector() {

            /**
             * 消息队列选择器,保证同一条业务数据的消息在同一个队列
             * @param mqs topic中所有队列的集合
             * @param msg 发送的消息
             * @param arg 此参数是本示例中producer.send的第三个参数
             * @return
             */
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                Integer id = (Integer) arg;
                // id == 1001
                int index = id % mqs.size();
                // 分区顺序:同一个模值的消息在同一个队列中
                return mqs.get(index);

                // 全局顺序:所有的消息都在同一个队列中
                // return mqs.get(mqs.size() - 1);
            }
        };

        String[] tags = new String[]{"TagA", "TagB", "TagC"};

        List<Map> bizDatas = getBizDatas();

        // 5:循环发送消息
        for (int i = 0; i < bizDatas.size(); i++) {
            Map bizData = bizDatas.get(i);
            // keys:业务数据的ID,比如用户ID、订单编号等等,根据msgType划分不同的分区
            Message msg = new Message("TopicTest", tags[i % tags.length], "" + bizData.get("msgType"), bizData.toString().getBytes(RemotingHelper.DEFAULT_CHARSET));
            // 发送有序消息
            SendResult sendResult = producer.send(msg, messageQueueSelector, bizData.get("msgType"));

            System.out.printf("%s, body:%s%n", sendResult, bizData);
        }

        // 6:关闭生产者
        producer.shutdown();
    }

    public static List<Map> getBizDatas() {
        List<Map> orders = new ArrayList<Map>();

        HashMap orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "存钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "取钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "存钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "存钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "存钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "取钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "取钱1000");
        orders.add(orderData);


        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "取钱1000");
        orders.add(orderData);

        orderData = new HashMap();
        orderData.put("msgType", 1001);
        orderData.put("userId", "tony");
        orderData.put("desc", "存钱1000");
        orders.add(orderData);

        return orders;
    }
}

image

package com.study.rocketmq.a153_order;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.*;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;

import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 顺序消息消费者
 */
public class OrderedConsumer {
    public static final String NAME_SERVER_ADDR = "192.168.100.242:9876";
    public static void main(String[] args) throws Exception {
        // 1. 创建消费者(Push)对象
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GROUP_TEST");

        // 2. 设置NameServer的地址,如果设置了环境变量NAMESRV_ADDR,可以省略此步
        consumer.setNamesrvAddr(NAME_SERVER_ADDR);

        /**
         * 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费<br>
         * 如果非第一次启动,那么按照上次消费的位置继续消费
         */
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        // 3. 订阅对应的主题和Tag
        consumer.subscribe("TopicTest", "TagA || TagB || TagC");

        // 4. 注册消息接收到Broker消息后的处理接口
        // 注1:普通消息消费 [[
//        consumer.registerMessageListener(new MessageListenerConcurrently() {
//            AtomicInteger count = new AtomicInteger(0);
//
//            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
//                doBiz(list.get(0));
//                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
//            }
//        });
        // ]] 注1:普通消息消费

        // consumer
        consumer.setMaxReconsumeTimes(-1);
        // 延时  level  3

        // 注2:顺序消息消费 [[
        consumer.registerMessageListener(new MessageListenerOrderly() {

            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
                                                       ConsumeOrderlyContext context) {
                context.setAutoCommit(true);
                doBiz(msgs.get(0));

                return ConsumeOrderlyStatus.SUCCESS;
            }
        });
        // ]] 注2:顺序消息消费

        // 5. 启动消费者(必须在注册完消息监听器后启动,否则会报错)
        consumer.start();

        System.out.println("已启动消费者");
    }

    /**
     * 模拟处理业务
     *
     * @param message
     */
    public static void doBiz(Message message) {
        try {
            System.out.printf("线程:%-25s 接收到新消息 %s --- %s %n", Thread.currentThread().getName(), message.getTags(), new String(message.getBody(), RemotingHelper.DEFAULT_CHARSET));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

image
image

如何保证消息的有序性

image
image

RockeMQ顺序消息的原理

image
image
通过锁来实现同步
image
image
http://rocketmq.apache.org/docs/order-example/
image

posted @ 2021-04-19 21:56  kopoo  阅读(172)  评论(0编辑  收藏  举报