RocketMQ原生API使用
测试环境搭建
<dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-client</artifactId> <version>4.7.1</version> </dependency>
RocketMQ的编程模型
然后RocketMQ的生产者和消费者的编程模型都是有个比较固定的步骤的,掌握这个固定的步骤,对 于我们学习源码以及以后使用都是很有帮助的。
消息发送者的固定步骤 1.创建消息生产者producer,并制定生产者组名 2.指定Nameserver地址 3.启动producer 4.创建消息对象,指定主题Topic、Tag和消息体 5.发送消息 6.关闭生产者producer
消息消费者的固定步骤 1.创建消费者Consumer,制定消费者组名 2.指定Nameserver地址 3.订阅主题Topic和Tag 4.设置回调函数,处理消息 5.启动消费者consumer
RocketMQ的消息样例
基本样例
基本样例部分我们使用消息生产者分别通过三种方式发送消息,同步发送、异步发送以及单向发送。
package vn.rocket.simple; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; /** * @author VN * 致敬未来的你 * @date 2022/7/20 20:58 * * 同步发送 */ @Slf4j public class Producer { /** * 消息发送者的固定步骤 * * 1.创建消息生产者producer,并制定生产者组名 * 2.指定Nameserver地址 * 3.启动producer * 4.创建消息对象,指定主题Topic、Tag和消息体 * 5.发送消息 * 6.关闭生产者producer */ public static void main(String[] args) throws MQClientException { DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName"); producer.setNamesrvAddr("localhost:9876"); producer.start(); for (int i = 0; i < 2; i++) { try { { // topic 发送的目标 // tags、keys 分组 // 发送内容 Message msg = new Message("TopicTest", "TagA", "OrderID188", "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); /* 同步发送,会返回producer一个消息,然后再往后面执行 */ SendResult sendResult = producer.send(msg); System.out.println(JSONUtil.toJsonStr(sendResult)); /* 单向发送,发送消息后没有返回值 */ // producer.sendOneway(msg); } } catch (Exception e) { e.printStackTrace(); } } producer.shutdown(); } }
package vn.rocket.simple; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendCallback; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; import java.io.UnsupportedEncodingException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * @author VN * 致敬未来的你 * @date 2022/7/20 22:41 * * 异步发送 */ @Slf4j public class AsyncProducer { public static void main( String[] args) throws MQClientException, InterruptedException, UnsupportedEncodingException { DefaultMQProducer producer = new DefaultMQProducer("Jodie_Daily_test"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); producer.setRetryTimesWhenSendAsyncFailed(0); int messageCount = 100; /* 由于是异步发送,这里引入一个countDownLatch,保证所有Producer发送消息的回调方法都执行完了再停止Producer服务 */ final CountDownLatch countDownLatch = new CountDownLatch(messageCount); for (int i = 0; i < messageCount; i++) { try { final int index = i; Message msg = new Message("vn", "TagA", "OrderID188", "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); producer.send(msg, new SendCallback() { // 成功 @Override public void onSuccess(SendResult sendResult) { countDownLatch.countDown(); log.info("SUCCESS同步发送返回具体信息index:{},MsgId:{}", index, sendResult.getMsgId()); } // 失败 @Override public void onException(Throwable e) { countDownLatch.countDown(); log.info("FAIL同步发送返回具体信息index:{},e:{}", index, e); e.printStackTrace(); } }); log.info("消息发送完成"); } catch (Exception e) { e.printStackTrace(); } } countDownLatch.await(5, TimeUnit.SECONDS); producer.shutdown(); } }
package vn.rocket.simple; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; /** * 推模式(监听模式) * * @author VN */ @Slf4j public class PushConsumer { public static void main(String[] args) throws InterruptedException, MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_JODIE_1"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.subscribe("TopicTest", "*"); /** * consumeFromWhere配置 * 1. CONSUME_FROM_FIRST_OFFSET:初次从消息队列头部开始消费,即历史消息(还存在broker的),全部消费一遍,后续再启动接着上次消费的进度开始消费 * 2. CONSUME_FROM_LAST_OFFSET:默认策略,初次从该队列最尾开始消费,即跳过历史消息,后续再启动接着上次消费的进度开始消费 * 3. CONSUME_FROM_TIMESTAMP:从某个时间点开始消费,默认是半个小时以前,后续再启动着上次消费的进度开始消费 */ consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); /** * 设置消费的时间点 * 可以设置从什么时间开始消费,配合setConsumeTimestamp一起使用默认半小时之前的 * 格式yyyyMMddhhmmss */ consumer.setConsumeTimestamp("20181109221800"); // 进行队列监听 consumer.registerMessageListener(new MessageListenerConcurrently() { // 消费队列中信息 @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { log.info("队列中的信息pushResult:{},msgs:{}", Thread.currentThread().getName(), msgs); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); log.info("Consumer Started"); } }
package vn.rocket.simple; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; import org.apache.rocketmq.client.consumer.PullResult; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.message.MessageQueue; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author VN * 致敬未来的你 * @date 2022/7/20 22:14 */ @Slf4j public class PullConsumer { /** * 消息消费者的固定步骤 * * 1.创建消费者Consumer,制定消费者组名 * 2.指定Nameserver地址 * 3.订阅主题Topic和Tag * 4.设置回调函数,处理消息 * 5.启动消费者consumer */ private static final Map<MessageQueue, Long> OFFSE_TABLE = new HashMap<MessageQueue, Long>(); public static void main(String[] args) throws MQClientException { int i = 0; DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("ProducerGroupName"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.start(); Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TopicTest"); for (MessageQueue mq : mqs) { log.info("获取的队列信息mq:{},count:{}", JSONUtil.toJsonStr(mq),i++); while (true) { try { PullResult pullResult = consumer.pullBlockIfNotFound(mq, null, // 过滤条件 getMessageQueueOffset(mq), // 便宜量 32); // 每次拉取最多32个消息 log.info("队列中的信息pullResult:{}", JSONUtil.toJsonStr(pullResult)); /* 将队列和具体信息放到 Map 中 */ putMessageQueueOffset(mq, pullResult.getNextBeginOffset()); switch (pullResult.getPullStatus()) { case FOUND: break; case NO_MATCHED_MSG: break; case NO_NEW_MSG: break; case OFFSET_ILLEGAL: break; default: break; } } catch (Exception e) { e.printStackTrace(); } } } consumer.shutdown(); } private static long getMessageQueueOffset(MessageQueue mq) { Long offset = OFFSE_TABLE.get(mq); if (offset != null) { return offset; } return 0; } private static void putMessageQueueOffset(MessageQueue mq, long offset) { OFFSE_TABLE.put(mq, offset); } }
package vn.rocket.simple; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * 拉模式(订阅):此种方式,自己管理偏移量 OFFSET * * @author VN */ @Slf4j public class LitePullConsumerAssign { public static volatile boolean running = true; public static void main(String[] args) throws Exception { DefaultLitePullConsumer litePullConsumer = new DefaultLitePullConsumer("please_rename_unique_group_name"); litePullConsumer.setNamesrvAddr("127.0.0.1:9876"); litePullConsumer.setAutoCommit(false); litePullConsumer.start(); Collection<MessageQueue> mqSet = litePullConsumer.fetchMessageQueues("TopicTest"); List<MessageQueue> list = new ArrayList<>(mqSet); List<MessageQueue> assignList = new ArrayList<>(); for (int i = 0; i < list.size() / 2; i++) { assignList.add(list.get(i)); } litePullConsumer.assign(assignList); litePullConsumer.seek(assignList.get(0), 10); try { while (running) { List<MessageExt> messageExts = litePullConsumer.poll(); log.info("获取的队列信息messageExts:{}", JSONUtil.toJsonStr(messageExts)); litePullConsumer.commitSync(); } } finally { litePullConsumer.shutdown(); } } }
package vn.rocket.simple; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultLitePullConsumer; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; /** * 拉模式(订阅):此种方式,不需要管理偏移量 OFFSET * * @author VN */ @Slf4j public class LitePullConsumerSubscribe { public static volatile boolean running = true; public static void main(String[] args) throws Exception { DefaultLitePullConsumer litePullConsumer = new DefaultLitePullConsumer("lite_pull_consumer_test"); litePullConsumer.setNamesrvAddr("127.0.0.1:9876"); litePullConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); litePullConsumer.subscribe("TopicTest", "*"); litePullConsumer.start(); try { while (running) { List<MessageExt> messageExts = litePullConsumer.poll(); log.info("获取的队列信息messageExts:{}", JSONUtil.toJsonStr(messageExts)); } } finally { litePullConsumer.shutdown(); } } }
顺序消息
package vn.rocket.order; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; 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.List; /** * 发送消息局部有序 * * @author VN */ @Slf4j public class LocalOrderProducer { public static void main(String[] args) throws UnsupportedEncodingException { try { DefaultMQProducer producer = new DefaultMQProducer("vn1"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); for (int i = 0; i < 10; i++) { int orderId = i; for(int j = 0 ; j <= 5 ; j ++){ Message msg = new Message("vn", "order_"+orderId, "KEY" + orderId, ("order_"+orderId+" step " + j).getBytes(RemotingHelper.DEFAULT_CHARSET)); SendResult sendResult = producer.send(msg, new MessageQueueSelector() { /* 将相同的orderId 存到同一个 队列中 */ // mqs 是 OrderTopicTest 下面所有的队列 // msg 是 当前msg信息存到 mq中 // arg 是 传入的参数,当前的 arg 指的是 orderId @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); log.info("发送返回具体信息sendResult:{}", JSONUtil.toJsonStr(sendResult)); } } producer.shutdown(); } catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) { e.printStackTrace(); } } }
package vn.rocket.order; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; /** * 接受消息局部有序 * * @author VN */ @Slf4j public class LocalOrderConsumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("vn"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); consumer.subscribe("vn", "*"); /* MessageListenerOrderly 保证局部有序 */ consumer.registerMessageListener(new MessageListenerOrderly() { @Override public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) { context.setAutoCommit(true); for(MessageExt msg:msgs){ log.info("收到消息内容body:{}", JSONUtil.toJsonStr(new String(msg.getBody()))); } return ConsumeOrderlyStatus.SUCCESS; } }); /* MessageListenerConcurrently 保证不了有序 */ // consumer.registerMessageListener(new MessageListenerConcurrently() { // @Override // public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { // for(MessageExt msg:msgs){ // System.out.println("收到消息内容 "+new String(msg.getBody())); // } // return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // } // }); consumer.start(); log.info("Consumer Started"); } }
广播消息
package vn.rocket.broad; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.protocol.heartbeat.MessageModel; import java.util.List; /** * 广播消息 * * @author VN */ @Slf4j public class BroadCastConsumer { public static void main(String[] args) throws InterruptedException, MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("vn"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); /* 广播消息配置 */ consumer.setMessageModel(MessageModel.BROADCASTING); consumer.subscribe("vn", "*"); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { for(MessageExt msg:msgs){ log.info("收到消息内容body:{}", JSONUtil.toJsonStr(new String(msg.getBody()))); } return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); log.info("Broadcast Consumer Started"); } }
延迟消息
package vn.rocket.delay; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; /** * 延迟消息 * * @author VN */ @Slf4j public class DelayProducer { public static void main(String[] args) throws MQClientException, InterruptedException { DefaultMQProducer producer = new DefaultMQProducer("delay"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); for (int i = 0; i < 2; i++) { try { Message msg = new Message("vn" , "TagA" , ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) ); /* 等级:messageDelayLevel = 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h */ // 3 代表延迟 10s msg.setDelayTimeLevel(3); SendResult sendResult = producer.send(msg); log.info("sendResult", JSONUtil.toJsonStr(sendResult)); } catch (Exception e) { e.printStackTrace(); Thread.sleep(1000); } } producer.shutdown(); } }
package vn.rocket.delay; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.consumer.ConsumeFromWhere; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; /** * 延迟消息 * * @author VN */ @Slf4j public class DelayConsumer { public static void main(String[] args) throws InterruptedException, MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("vn"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET); consumer.subscribe("vn", "*"); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { log.info("Thread:{},msgs:{}", Thread.currentThread().getName(),JSONUtil.toJsonStr(msgs)); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); log.info("Consumer Started"); } }
批量消息
package vn.rocket.batch; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message; import java.util.ArrayList; import java.util.List; /** * 批量消息 * * @author VN */ public class SimpleBatchProducer { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); String topic = "vn"; 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); producer.shutdown(); } }
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package vn.rocket.batch; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.common.message.Message; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; /** * 批量发送消息,当消息 > 4M 的时候,进行分拆 * * @author VN */ public class SplitBatchProducer { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName"); producer.setNamesrvAddr("192.168.10.5:9876"); producer.start(); String topic = "BatchTest"; List<Message> messages = new ArrayList<>(100 * 1000); for (int i = 0; i < 100 * 1000; i++) { messages.add(new Message(topic, "Tag", "OrderID" + i, ("Hello world " + i).getBytes())); } /* 因为 messages > 4M 那么会报错 producer.send(messages); */ ListSplitter splitter = new ListSplitter(messages); while (splitter.hasNext()) { List<Message> listItem = splitter.next(); producer.send(listItem); } producer.shutdown(); } } /** * 对 messages进行分拆 */ class ListSplitter implements Iterator<List<Message>> { private int sizeLimit = 1000 * 1000; private final List<Message> messages; private int currIndex; public ListSplitter(List<Message> messages) { this.messages = messages; } @Override public boolean hasNext() { return currIndex < messages.size(); } @Override public List<Message> next() { int nextIndex = currIndex; int totalSize = 0; for (; nextIndex < messages.size(); nextIndex++) { Message message = messages.get(nextIndex); int tmpSize = message.getTopic().length() + message.getBody().length; Map<String, String> properties = message.getProperties(); for (Map.Entry<String, String> entry : properties.entrySet()) { tmpSize += entry.getKey().length() + entry.getValue().length(); } tmpSize = tmpSize + 20; if (tmpSize > sizeLimit) { if (nextIndex - currIndex == 0) { nextIndex++; } break; } if (tmpSize + totalSize > sizeLimit) { break; } else { totalSize += tmpSize; } } List<Message> subList = messages.subList(currIndex, nextIndex); currIndex = nextIndex; return subList; } @Override public void remove() { throw new UnsupportedOperationException("Not allowed to remove"); } }
过滤消息
package vn.rocket.filter; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; /** * 过滤消息 * * @author VN */ @Slf4j public class TagFilterProducer { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("filter"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); String[] tags = new String[] {"TagA", "TagB", "TagC"}; for (int i = 0; i < 15; i++) { Message msg = new Message("TagFilterTest", tags[i % tags.length], "Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET)); SendResult sendResult = producer.send(msg); log.info("sendResult:{}", sendResult); } producer.shutdown(); } } /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package vn.rocket.filter; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.common.message.MessageExt; import java.io.IOException; import java.util.List; /** * 过滤消息 * * @author VN */ @Slf4j public class TagFilterConsumer { public static void main(String[] args) throws InterruptedException, MQClientException, IOException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name"); consumer.setNamesrvAddr("127.0.0.1:9876"); consumer.subscribe("TagFilterTest", "TagA || TagC"); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { log.info("Thread:{},msgs:{}", Thread.currentThread().getName(), JSONUtil.toJsonStr(msgs)); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); System.out.printf("Consumer Started.%n"); } }
package vn.rocket.filter; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; /** *过滤消息 SQL * * @author VN */ @Slf4j public class SqlFilterProducer { /* RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。 数值比较,比如:>,>=,<,<=,BETWEEN,=; 字符比较,比如:=,<>,IN; IS NULL 或者 IS NOT NULL; 逻辑符号 AND,OR,NOT; 常量支持类型为: 数值,比如:123,3.1415 */ public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("filter"); producer.setNamesrvAddr("127.0.0.1:9876"); producer.start(); String[] tags = new String[] {"TagA", "TagB", "TagC"}; for (int i = 0; i < 15; i++) { Message msg = new Message("SqlFilterTest", tags[i % tags.length], ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) ); msg.putUserProperty("a", String.valueOf(i)); SendResult sendResult = producer.send(msg); log.info("sendResult:{}", sendResult); } producer.shutdown(); } } /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package vn.rocket.filter; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer; import org.apache.rocketmq.client.consumer.MessageSelector; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext; import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus; import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently; import org.apache.rocketmq.common.message.MessageExt; import java.util.List; /** * 过滤消息 SQL * * 使用注意:只有推模式的消费者可以使用SQL过滤。拉模式是用不了的 * * @author VN */ @Slf4j public class SqlFilterConsumer { /** * 报错:The broker does not support consumer to filter message by SQL92 * * 解决方案:修改broker配置文件 * * ###1.编辑broker.conf文件 * vim broker * ####2.添加属性 * enablePropertyFilter=true * */ public static void main(String[] args) throws Exception { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name"); consumer.setNamesrvAddr("192.168.10.5:9876"); /* RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。 数值比较,比如:>,>=,<,<=,BETWEEN,=; 字符比较,比如:=,<>,IN; IS NULL 或者 IS NOT NULL; 逻辑符号 AND,OR,NOT; 常量支持类型为: 数值,比如:123,3.1415 */ // 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)")); consumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { log.info("Thread:{},msgs:{}", Thread.currentThread().getName(), JSONUtil.toJsonStr(msgs)); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } }); consumer.start(); System.out.printf("Consumer Started.%n"); } }
事务消息
package vn.rocket.transaction; import cn.hutool.json.JSONUtil; import lombok.extern.slf4j.Slf4j; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.TransactionListener; import org.apache.rocketmq.client.producer.TransactionMQProducer; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.remoting.common.RemotingHelper; import java.io.UnsupportedEncodingException; import java.util.concurrent.*; /** * 事务消息 * * @author VN */ @Slf4j public class TransactionProducer { public static void main(String[] args) throws MQClientException, InterruptedException { TransactionListener transactionListener = new TransactionListenerImpl(); TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name"); producer.setNamesrvAddr("127.0.0.1:9876"); 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; } }); producer.setExecutorService(executorService); producer.setTransactionListener(transactionListener); 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); log.info("sendResult:{}", JSONUtil.toJsonStr(sendResult)); Thread.sleep(10); } catch (MQClientException | UnsupportedEncodingException e) { e.printStackTrace(); } } for (int i = 0; i < 100000; i++) { Thread.sleep(1000); } producer.shutdown(); } }
package vn.rocket.transaction; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.client.producer.TransactionListener; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; /** * 事务消息 * * @author VN */ @Slf4j public class TransactionListenerImpl implements TransactionListener { //在提交完事务消息后执行。 //返回COMMIT_MESSAGE状态的消息会立即被消费者消费到。 //返回ROLLBACK_MESSAGE状态的消息会被丢弃。 //返回UNKNOWN状态的消息会由Broker过一段时间再来回查事务的状态。 @Override public LocalTransactionState executeLocalTransaction(Message msg, Object arg) { String tags = msg.getTags(); //TagA的消息会立即被消费者消费到 if (StringUtils.contains(tags, "TagA")) { return LocalTransactionState.COMMIT_MESSAGE; //TagB的消息会被丢弃 } else if (StringUtils.contains(tags, "TagB")) { return LocalTransactionState.ROLLBACK_MESSAGE; //其他消息会等待Broker进行事务状态回查。 } else { return LocalTransactionState.UNKNOW; } } //在对UNKNOWN状态的消息进行状态回查时执行。返回的结果是一样的 @Override public LocalTransactionState checkLocalTransaction(MessageExt msg) { String tags = msg.getTags(); //TagC的消息过一段时间会被消费者消费到 if(StringUtils.contains(tags,"TagC")){ return LocalTransactionState.COMMIT_MESSAGE; //TagD的消息也会在状态回查时被丢弃掉 }else if(StringUtils.contains(tags,"TagD")){ return LocalTransactionState.ROLLBACK_MESSAGE; //剩下TagE的消息会在多次状态回查后最终丢弃 }else{ return LocalTransactionState.UNKNOW; } } }
事务消息的使用限制 1、事务消息不支持延迟消息和批量消息。 2、为了避免单个消息被检查太多次而导致半队列消息累积,我们默认将单个消息的检查次数限制为 15 次,但是用户可以通过 Broker 配置文件的 transactionCheckMax参数来修改此限制。如果已经检查某条消息超过 N 次的话( N = transactionCheckMax ) 则 Broker 将丢弃此消 息,并在默认情况下同时打印错误日志。用户可以通过重写 AbstractTransactionCheckListener 类来修改这个行为。 回查次数是由BrokerConfig.transactionCheckMax这个参数来配置的,默认15次,可以在broker.conf中覆盖。 然后实际的检查次数会在message中保存一个用户属性MessageConst.PROPERTY_TRANSACTION_CHECK_TIMES。这个属性值大于transactionC heckMax,就会丢弃。 这个用户属性值会按回查次数递增,也可以在Producer中自行覆盖这个属性。 3、事务消息将在 Broker 配置文件中的参数 transactionMsgTimeout 这样的特定时间长度之后被检查。当发送事务消息时,用户还可以通过设 置用户属性 CHECK_IMMUNITY_TIME_IN_SECONDS 来改变这个限制,该参数优先于 transactionMsgTimeout 参数。 由BrokerConfig.transactionTimeOut这个参数来配置。默认6秒,可以在broker.conf中进行修改。 另外,也可以给消息配置一个MessageConst.PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS属性来给消息指定一个特定的消息回查时间。 msg.putUserProperty(MessageConst.PROPERTY_CHECK_IMMUNITY_TIME_IN_SECONDS, "10000"); 这样就是10秒。 4、事务性消息可能不止一次被检查或消费。 5、提交给用户的目标主题消息可能会失败,目前这依日志的记录而定。它的高可用性通过 RocketMQ 本身的高可用性机制来保证,如果希望确保事务 消息不丢失、并且事务完整性得到保证,建议使用同步的双重写入机制。 6、事务消息的生产者 ID 不能与其他类型消息的生产者 ID 共享。与其他类型的消息不同,事务消息允许反向查询、MQ服务器能通过它们的生产者 ID 查询到消费者。

报错:
The broker does not support consumer to filter message by SQL92
解决方案:修改broker配置文件 1.编辑broker.conf文件 vim broker vim broker-a.properties vim broker-b.properties vim broker-trace.properties 2.添加属性 enablePropertyFilter=true 3.启动broker nohup sh mqbroker -c /opt/software/rocketmq/conf/2m-noslave/broker-a.properties >/dev/null 2>&1 &


浙公网安备 33010602011771号