在RocketMQ中,TAG是一个简单而有用的设计,其可以来选择您想要的消息。例如:
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("defultGroup");
consumer.subscribe("Topic", "TagA || TagB || TagC");
消费者将接收包含 TagA 或 TagB 或 TagC 的消息。但是限制是一个消息只能有一个标签,这对于复杂的场景可能不起作用。
在这种情况下,可以使用SQL92表达式筛选消息。SQL92特性可以通过发送消息时的属性来进行计算。在RocketMQ定义的语法下,可以实现一些简单的逻辑。下面是一个例子:
------------
| message |
|----------| a > 5 AND b = 'abc'
| a = 10 | --------------------> Gotten
| b = 'abc'|
| c = true |
------------
------------
| message |
|----------| a > 5 AND b = 'abc'
| a = 1 | --------------------> Missed
| b = 'abc'|
| c = true |
------------
SQL92基本语法
RocketMQ只定义了一些基本语法来支持这个特性。你也可以很容易地扩展它。
- 数值比较,比如:>,>=,<,<=,BETWEEN,=;
- 字符比较,比如:=,<>,IN;
- IS NULL 或者 IS NOT NULL;
- 逻辑符号 AND,OR,NOT;
常量支持类型为:
- 数值,比如:123,3.1415;
- 字符,比如:‘abc’,必须用单引号包裹起来;
- NULL,特殊的常量
- 布尔值,TRUE 或 FALSE
使用限制
只有push模式的消费者支持使用 SQL92 进行消息过滤,接口信息如下:
public void subscribe(finalString topic, final MessageSelector messageSelector)
示例
过滤消息的生产者-Producer
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
// 创建 producer 实例,并设置生产者组名
DefaultMQProducer producer = new DefaultMQProducer("filterGroup");
// 设置 NameServer 地址
producer.setNamesrvAddr("127.0.0.1:9876");
// 启动生产者
producer.start();
for (int i = 0; i < 10; i++) {
Message message = new Message("filterTopic", "TagA", ("Hello filter message-" + i).getBytes());
// 发送消息时,通过`putUserProperty`来设置消息的属性
message.putUserProperty("num", String.valueOf(i));
SendResult sendResult = producer.send(message);
System.out.println(String.format("SendResult status:%s, queueId:%d",
sendResult.getSendStatus(),
sendResult.getMessageQueue().getQueueId()));
}
// 关闭生产者
producer.shutdown();
}
生成消息的时候,message.putUserProperty("num", String.valueOf(i));
设置一个属性,这个属性在消费的时候会拿来写SQL语句,详情见下方消费示例。
过滤消息的消费者者-Consumer
public static void main(String[] args) throws MQClientException {
// 实例化消费者,并设置组名
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("filterGroup");
// 设置 NameServer 地址
consumer.setNamesrvAddr("127.0.0.1:9876");
// 订阅 Topic
consumer.subscribe("filterTopic", MessageSelector.bySql("TAGS = 'TagA' and num between 2 and 5"));
// 注册消息监听者
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> messages, ConsumeConcurrentlyContext context) {
for (MessageExt message : messages) {
// 打印消息内容
System.out.println("Receive message[msgBody=" + new String(message.getBody()) + "] ");
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 启动消费者
consumer.start();
}
消息订阅的时候使用了 MessageSelector.bySql();
表明使用 SQL92 的过滤模式,然后传入的字符串是符合 SQL92 语法的语句。
对消息进行过滤的SQL表达式由2个条件组成:
- TAGS = ‘TagA’
- num between 2 and 5
这个“TAGS”是关键字,为了兼容tag过滤,在SQL92过滤中,使用TAGS替代tag,然后“num”是消息生产时 message.putUserProperty("num", String.valueOf(i));
设置的属性。
如果我的生产者有多个tag:
String[] tags = new String[] {"TagA", "TagB", "TagC"};
// 依次选择 tag
Message message = new Message("filterTopic", tags[i % tags.length], ("Hello filter message-" + i).getBytes());
// 发送消息时,通过`putUserProperty`来设置消息的属性
message.putUserProperty("num", String.valueOf(i));
那么消费者可以这样过滤:
// 订阅 Topic
consumer.subscribe("filterTopic", MessageSelector.bySql("TAGS in ('TagA', 'TagB') and num between 2 and 5"));
灵活的使用 SQL 表达式
如果在启动消费者时,出现下面这错误,
Exception in thread "main" org.apache.rocketmq.client.exception.MQClientException: CODE: 1 DESC: The broker does not support consumer to filter message by SQL92
For more information, please visit the url, http://rocketmq.apache.org/docs/faq/
则表示当前broker没有开启过滤支持,需要开启。
如果部署了监控平台,也可以在监控平台上查看有没有开启过滤支持,如下图所示:
开启 broker 过滤支持,只需要在 broker 的配置文件中增加一项配置即可:
enablePropertyFilter=true
如果有主从,也就是有多个 broker的话,需要在没一个 broker 配置文件里面都加上这句话。
vim /usr/local/rocketmq/conf/2m-2s-sync/broker-a.properties
vim /usr/local/rocketmq/conf/2m-2s-sync/broker-a-s.properties
vim /usr/local/rocketmq/conf/2m-2s-sync/broker-b.properties
vim /usr/local/rocketmq/conf/2m-2s-sync/broker-b-s.properties
最后再重启 broker 就行了。
在监控平台上可以看见开启状态,如下图示:
技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。