消费过滤

一、为什么要涉及过滤功能?

RocketMQ 设计了消息过滤,来解决大量无意义流量的传输:即对于客户端不需要的消息,Broker 就不会传输给客户端,以免浪费宽带。

二、RocketMQ 支持消息过滤

  RocketMQ 4.8.0 支持 Tag 过滤、SQL92过滤、Filter Server过滤。

  RocketMQ Tag 过滤流程:

kafka增加过滤条件消费 kafka tag过滤_Server

  第一步:用户发送一个带 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 过滤流程:

kafka增加过滤条件消费 kafka tag过滤_kafka增加过滤条件消费_02

  第一步:消费者订阅 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 过滤流程(一种不常用但是非常灵活的过滤方式):

kafka增加过滤条件消费 kafka tag过滤_Server_03

  第一步:用户消费者从 Namesrv 获取 Topic 路由信息,同时上传自定义的过滤器实现类源代码到 Filter Server 中,Filter Server 编译并实例化过滤器类。

  第二步:用户发送拉取消息请求到 Filter Server,Filter Server 通过 pull consumer 从 Broker 拉取消息,执行过滤类中的过滤方法,返回过滤后的消息。