RocketMQ的过滤方式主要分为如下2种,其中类模式过滤的方式会在RocketMQ 5.0.0版本中被移除,所以不进行详细分析;
public class TagFilterProducer {
public static final String RPODUCER_GROUP_NAME = "tagFilterProducerGroup";
public static final String TOPIC_NAME = "tagFilterTopic";
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer(RPODUCER_GROUP_NAME);
producer.setNamesrvAddr("192.168.199.138:9876");
producer.setSendMsgTimeout(6000);
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC"};
for (int i = 0; i < 12; i++) {
Message message = new Message(TOPIC_NAME, tags[i % tags.length], ("hello rocketmq " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(message);
System.out.println(sendResult);
}
producer.shutdown();
}
}
Producer端总共发送了12条数据。TagA,TagB,TagC的数据各4条。
public class TagFilterConsumer {
public static final String CONSUMER_GROUP_NAME = "tagFilterConsumerGroup";
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_GROUP_NAME);
consumer.setNamesrvAddr("192.168.199.138:9876");
consumer.subscribe(TagFilterProducer.TOPIC_NAME, "TagA || TagC");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.printf("%s receive new message %s", Thread.currentThread().getName(), list);
System.out.println();
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("Consumer Started");
}
}
Consumer端订阅了TagA和TagC的数据,最终只接收到8条数据,TagA和TagC的数据各4条
在发送消息的时候,可以给消息设置很多扩展属性,SQL92语法过滤就是根据这些扩展属性来过滤消息的(其实tag也是扩展属性的一种)
rocketmq默认是不支持sql92语法过滤的,需要在broker.conf中加入如下配置
enablePropertyFilter = true
filterSupportRetry = true
public class SqlFilterProducer {
public static final String RPODUCER_GROUP_NAME = "sqlFilterProducerGroup";
public static final String TOPIC_NAME = "sqlFilterTopic";
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer(RPODUCER_GROUP_NAME);
producer.setNamesrvAddr("192.168.199.138:9876");
producer.setSendMsgTimeout(6000);
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC"};
for (int i = 0; i < 12; i++) {
Message message = new Message(TOPIC_NAME, tags[i % tags.length], ("hello rocketmq " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
message.putUserProperty("a", String.valueOf(i));
SendResult sendResult = producer.send(message);
System.out.println(sendResult);
}
producer.shutdown();
}
}
public class SqlFilterConsumer {
public static final String CONSUMER_GROUP_NAME = "sqlFilterConsumerGroup";
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(CONSUMER_GROUP_NAME);
consumer.setNamesrvAddr("192.168.199.138:9876");
consumer.subscribe(SqlFilterProducer.TOPIC_NAME,
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> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.printf("%s receive new message %s \n", Thread.currentThread().getName(), list);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.println("Consumer Started");
}
}
生产者发送了12条数据,消费者消费了2条数据(tag为TagA或TagB,属性a的值在0到3之间)
因为通过tag过滤的方式最常用,因此只分析tag过滤的实现过程。
如果让你实现通过tag过滤你会在Consumer端进行过滤还是在Broker端进行过滤?
可能大多数人都会选择在Broker端进行过滤,因为在Consumer端进行过滤,会有无用的消息被发送到Consumer端,造成带宽浪费
其实RocketMQ根据Tag过滤消息的方式需要Consumer端和Broker端一起来完成
首先tag的hashcode冲突的概率比较低。其次,你完全可以在设置tagName的时候避免和之前tagName的hashcode产生冲突;