RocketMQ的消息过滤功能是如何实现的呢?

432 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情

1 初衷

RocketMQ设计了消息过滤来解决消费者的定制化消费,当消费者需要某一种消息时,Broker就会剔除不不需要的消息,并只将这类消息传输给客户端,以免浪费带宽。

2 Tag过滤

步骤:

  1. 用户发送一个带Tag的消息
  2. 用户订阅一个Topic的Tag,Broker会保存这个订阅关系。
  3. 在Broker端做Tag过滤。当消费者在Pull消息的时候,Broker会根据Tag的HashCode进行对比。若是不满足条件,则不会将消息发送给消费者。
  4. 客户端进行Tag过滤,进行字符串比对来做第二次Tag过滤。

这里由于HashCode比对存在哈希碰撞的缘故,所以需要在客户端再进行一次过滤。以确定处理的消息均为需要的消息

订阅topic的时候可以指定tag名字

//订阅topic
consumer.subscribe("topic_luke","tag");

为*时不过滤任何消息,需要订阅多个tag用||隔开,例如tag_a||tag_b
org.apache.rocketmq.client.impl.consumer.PullAPIWrapper#processPullResult中有过滤tag的逻辑。

if (!subscriptionData.getTagsSet().isEmpty() && !subscriptionData.isClassFilterMode()) {
    msgListFilterAgain = new ArrayList<MessageExt>(msgList.size());
    for (MessageExt msg : msgList) {
        if (msg.getTags() != null) {
            if (subscriptionData.getTagsSet().contains(msg.getTags())) {
                msgListFilterAgain.add(msg);
            }
        }
    }
}

3 SQL过滤

步骤:

  1. 消费者订阅Topic,上传SQL过滤语句。Broker会编译SQL并保存。
  2. 消费者Pull消息
  3. 第一次过滤,使用Bloom过滤器的isHit()方法做第一次过滤,这个和HashCode作比较一样,会过滤大部分不需要的消息,过滤之后的消息也不全是需要的
  4. 第二次过滤,执行编译之后SQL方法的evaluate()即可得到需要的消息。

使用SQL过滤的时候,在Broker配置中需要加入如下参数:

enableConsumeQueueExt = true;
filterSupportRetry = true;
enablePropertyFilter = true;
enableCalcFilterBitMap = true;

订阅Topic的时候可以指定SQL语句

consumer.subscribe("topic_luke","sex IS NOT NULL and sex = 1");

这里的语法支持为:

  • 数值比较,比如:>>=<<=BETWEEN=
  • 字符比较,比如:=<>IN
  • IS NULL或者 IS NOT NULL;
  • 逻辑符号 ANDORNOT

常量支持类型为:

  • 数值,比如:1233.1415
  • 字符,比如:'abc',必须用单引号包裹起来;
  • NULL,特殊的常量;
  • 布尔值,TRUE 或 FALSE