消费过滤
一、为什么要涉及过滤功能?
RocketMQ 设计了消息过滤,来解决大量无意义流量的传输:即对于客户端不需要的消息,Broker 就不会传输给客户端,以免浪费宽带。
二、RocketMQ 支持消息过滤
RocketMQ 4.8.0 支持 Tag 过滤、SQL92过滤、Filter Server过滤。
RocketMQ Tag 过滤流程:
第一步:用户发送一个带 Tag 的消息。
第二步:用户订阅一个 Topic 的 Tag,RocketMQ Broker 会保存订阅关系。
第三步:在 Broker 端做 Tag 过滤。消费者在 Pull 消费时,RocketMQ Broker 会根据 Tag 的 Hashcode 进行对比。如果不满足条件,消息不会返回给消费者,以节约宽带。
第四步:客户端 Tag 过滤。Hash 对不同的 Tag 计算出来的 Hash 值可能是一样的,在这种情况下过滤后的消息是错误的,所以 RocketMQ 设计了客户端字符串对比功能,用来做第二次 Tag 过滤。
Tag 过滤为什么设计成 Broker 端使用 Hash 过滤,而客户端使用 Tag 字符串进行对比过滤呢?
Broker 端使用 Hash 过滤可以快速过滤海量消息,即使偶尔有 "落网之鱼",在客户端字符串过滤后也可能被成功过滤。
消费者如何订阅带 Tag 的 Topic 呢?
1 public class Consumer {
2
3 public static void main(String[] args) throws InterruptedException, MQClientException {
4
5 // 实例化消费者
6 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");
7
8 // 设置NameServer的地址
9 consumer.setNamesrvAddr("localhost:9876");
10
11 // 订阅一个或者多个Topic,以及Tag来过滤需要消费的消息
12 consumer.subscribe("TopicTest", "*");
13 // 注册回调实现类来处理从broker拉取回来的消息
14 consumer.registerMessageListener(new MessageListenerConcurrently() {
15 @Override
16 public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
17 System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
18 // 标记该消息已经被成功消费
19 return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
20 }
21 });
22 // 启动消费者实例
23 consumer.start();
24 System.out.printf("Consumer Started.%n");
25 }
26 }
RocketMQ SQL 过滤流程:
第一步:消费者订阅 Topic,上传过滤 SQL 语句,RocketMQ Broker 编译 SQL 保存。
第二步:消费者 Pull 消费。
第一次过滤:使用 Bloom 过滤器的 isHit() 方法做第一次过滤。Bloom 过滤器效率高,但是也存在缺陷,即只能判断不需要的消息,过滤后的消息也不保证都是需要消费的。
第二次过滤:执行编译后 SQL 方法 evaluae() 即可过滤出最终的结果。
在使用 SQL 过滤前,需要在启动 Broker 时配置如下几个参数:
1 enableConsumeQueueExt = true; #是否启用ConsumeQueue扩展属性
2 filterSupportRetry = true; #消息过滤是否支持重试
3 enablePropertyFilter = true; #是否支持根据属性过滤。如果使用基于标准的sql92模式过滤消息则改参数必须设置为true。
4 enableCalcFilterBitMap = true; #是否开启比特位映射
RocketMQ Filter Server 过滤流程(一种不常用但是非常灵活的过滤方式):
第一步:用户消费者从 Namesrv 获取 Topic 路由信息,同时上传自定义的过滤器实现类源代码到 Filter Server 中,Filter Server 编译并实例化过滤器类。
第二步:用户发送拉取消息请求到 Filter Server,Filter Server 通过 pull consumer 从 Broker 拉取消息,执行过滤类中的过滤方法,返回过滤后的消息。