002-RocketMQ-消息类型及示例解析

消息类型

基本消息

同步发送消息

DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.start();

for (int i = 0; i < 128; i++)
    try {
        {
            Message msg = new Message("TopicTest",
                "TagA",
                "OrderID188",
                "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
            SendResult sendResult = producer.send(msg);
            System.out.printf("%s%n", sendResult);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

producer.shutdown();

异步发送消息

Message msg = new Message("Jodie_topic_1023",
    "TagA",
    "OrderID188",
    "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg, new SendCallback() {
    @Override
    public void onSuccess(SendResult sendResult) {
        countDownLatch.countDown();
        System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
    }

    @Override
    public void onException(Throwable e) {
        countDownLatch.countDown();
        System.out.printf("%-10d Exception %s %n", index, e);
        e.printStackTrace();
    }
});

和普通消息相比send的时候多设置了一个SendCallback
指定
成功了回调:onSuccess
失败了回调:onException

单向消息

发送消息调用
producer.sendOneway(msg);
只管发送消息没有返回值也没有回调

顺序消息

很多时候消息之间可能是存在顺序的
比如一个订单的多种状态流转,如果异步来做还是希望消息按顺序接受

发送端

发送端要把有先后关系的消息发送到同一个MessageQueue

String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 100; i++) {
    int orderId = i % 10;
    Message msg =
        new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
            ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
    SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
        @Override
        public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
            Integer id = (Integer) arg;
            int index = id % mqs.size();
            return mqs.get(index);
        }
    }, orderId);
}

在MessageQueueSelector实现里实现了通过orderId选择MessageQueue的逻辑

消费者

既然已经在一个队列里了那么消费者就按顺序消费即可
其实是消费者对MessageQueue上锁并拉取批量消息消费

consumer.registerMessageListener(new MessageListenerOrderly() {
            AtomicLong consumeTimes = new AtomicLong(0);

            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
               //业务处理
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });

注册的类型为MessageListenerOrderly

广播消息

consumer.setMessageModel(MessageModel.BROADCASTING);  
consumer.registerMessageListener(new MessageListenerConcurrently() {

    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
        ConsumeConcurrentlyContext context) {
        System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

设置消息类型为广播、注册ConsumeConcurrentlyContext 异步处理消息监听
此时管理offset改为broker进行管理,所有注册的consumer处理完了就把offset增加

延时消息

消息发送到Broker后先把消息存储到一个系统队列中,有对应的定时任务定时拉取该被消费的消息给对应的业务group

发送

// Instantiate a producer to send scheduled messages
DefaultMQProducer producer = new DefaultMQProducer("ExampleProducerGroup");
// Launch producer
producer.start();
int totalMessagesToSend = 100;
for (int i = 0; i < totalMessagesToSend; i++) {
    Message message = new Message("TestTopic", ("Hello scheduled message " + i).getBytes());
    // This message will be delivered to consumer 10 seconds later.
    message.setDelayTimeLevel(3);
    // Send the message
    producer.send(message);
}

// Shutdown producer after use.
producer.shutdown();

主要就是设置了延时类型message.setDelayTimeLevel(3);
这个3指的是第三类型的延时时间,在配置中可以看类型和延时时间的关系
默认为:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

批量消息

减少网络IO
但是有要求:

  1. 大小不能超过1M (实际发送端限制是4M)
  2. 消息应该有相同的Topic,相同的waitStoreMsgOK
  3. 不能是延迟消息、事务消息
DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName");
producer.start();

//If you just send messages of no more than 1MiB at a time, it is easy to use batch
//Messages of the same batch should have: same topic, same waitStoreMsgOK and no schedule support
String topic = "BatchTest";
List<Message> messages = new ArrayList<>();
messages.add(new Message(topic, "Tag", "OrderID001", "Hello world 0".getBytes()));
messages.add(new Message(topic, "Tag", "OrderID002", "Hello world 1".getBytes()));
messages.add(new Message(topic, "Tag", "OrderID003", "Hello world 2".getBytes()));

producer.send(messages);

过滤消息

可以通过

  1. 使用Tag过滤
  2. 使用SQL过滤

这些过滤条件虽然是consumer设置的
但是其实处理的是broker
这样可以节省带宽

tag过滤

consumer.subscribe("TagFilterTest", "TagA || TagC");

消费端设置监听的tag,其他类型不处理

SQL过滤

consumer.subscribe("SqlFilterTest",
            MessageSelector.bySql("(TAGS is not null and TAGS in ('TagA', 'TagB')) and (a is not null and a between 0 and 3)"));

消费端按SQL的where部分设置监听

事务消息

只保证了发送端的事务
消费端可以靠MQ本来就有的最终一致性保证

发送端

消息投递状态:
LocalTransactionState.UNKNOW:未知
LocalTransactionState.COMMIT_MESSAGE:成功
LocalTransactionState.ROLLBACK_MESSAGE:失败

消费端监听消息处理为TransactionListener
有2个方法
executeLocalTransaction:提交消息后立即执行,返回消息投递状态
checkLocalTransaction: 如果立即执行返回的状态是未知,则进入阶梯等待时间回调此方法,共15次,可在这个方法里定制事务检查策略,如查表等方案

限制:不支持批量和延时消息

public static void main(String[] args) throws Exception {
		//自定义线程池 专门处理事物回查、不过不设置则自动创建一个
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("client-transaction-msg-check-thread");
                return thread;
            }
        });

        //这里发送端改了 是事物专用的发送端
        TransactionMQProducer producer = new TransactionMQProducer("lz_demo_test_group");
        producer.setNamesrvAddr("注册中心地址和端口");  //(2)
        producer.setExecutorService(executorService);
        producer.setTransactionListener(new TransactionListenerImpl());
        producer.start();

        String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 10; i++) {
            try {
                Message msg = new Message("TopicTest", tags[i % tags.length], "KEY" + i,
                                ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
                SendResult sendResult = producer.sendMessageInTransaction(msg, null);
                System.out.printf("%s%n", sendResult);

                Thread.sleep(10);
            } catch (MQClientException | UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        for (int i = 0; i < 100000; i++) {
            Thread.sleep(1000);
        }
        producer.shutdown();
    }

    static class TransactionListenerImpl implements TransactionListener {
        private AtomicInteger transactionIndex = new AtomicInteger(0);

        private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();

        /**
         * 半事物消息提交给MQ后 执行此方法
         * 官方建议在这里 执行本地事物
         * 可能需要行为参数化
         */
        @Override
        public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
            int value = transactionIndex.getAndIncrement();
            int status = value % 3;
            localTrans.put(msg.getTransactionId(), status);
            return LocalTransactionState.UNKNOW;
        }

        /**
         * 本地事物是否提交查询
         */
        @Override
        public LocalTransactionState checkLocalTransaction(MessageExt msg) {
            Integer status = localTrans.get(msg.getTransactionId());
            if (null != status) {
                switch (status) {
                    case 0:
                        return LocalTransactionState.UNKNOW;
                    case 1:
                        return LocalTransactionState.COMMIT_MESSAGE;
                    case 2:
                        return LocalTransactionState.ROLLBACK_MESSAGE;
                    default:
                        return LocalTransactionState.COMMIT_MESSAGE;
                }
            }
            return LocalTransactionState.COMMIT_MESSAGE;
        }
    }

事务消息处理过程

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值