tag过滤可参照:http://www.xunyajie.com/2019/03/30/RocketMQ%E6%B6%88%E6%81%AF%E8%BF%87%E6%BB%A4%E5%88%86%E6%9E%90/
本文着重补充这个博客后面未写完的内容。(sql过滤原理)
引言
RocketMQ支持表达式过滤以及类过滤两种,表达式过滤又分为TAG和SQL92。
MessageFilter类主要有两个方法:
- isMatchedByConsumeQueue(final Long tagsCode, final ConsumeQueueExt.CqExtUnit cqExtUnit);
- isMatchedByCommitLog(final ByteBuffer msgBuffer, final Map<String, String> properties);
RocketMQ中消息过滤是在订阅的时候做的。下面抛出问题:
1:记得在看消息消费consumer端逻辑的时候,那里面有对tag的过滤,那个过滤和这个过滤有什么区别?
ps:
Tag过滤方式:Consumer端在订阅消息时除了指定Topic还可以指定TAG,如果一个消息有多个TAG,可以用||分隔。其中,Consumer端会将这个订阅请求构建成一个 SubscriptionData,发送一个Pull消息的请求给Broker端。Broker端从RocketMQ的文件存储层—Store读取数据之前,会用这些数据先构建一个MessageFilter,然后传给Store。Store从 ConsumeQueue读取到一条记录后,会用它记录的消息tag hash值去做过滤,由于在服务端只是根据hashcode进行判断,无法精确对tag原始字符串进行过滤,故在消息消费端拉取到消息后,还需要对消息的原始tag字符串进行比对,如果不同,则丢弃该消息,不进行消息消费。
2:是否意味着消息消费broker端已经将过滤好的消息传给了consumer?
是的,如果是基于tag过滤,那么broker会基于tag的hashCode进行过滤
3:过滤表达式怎么同步到服务端?
在启动的时候会更新一次
在每次PullTaskImpl执行的时候会上传表达式,所以理论上,过滤表达式是可以实时更新的。
如果是文件形式的过滤,在每次心跳的时候会上传到服务端
根据源码剖析,先抛一下结论:
TAG/propertiesSQL过滤都是在服务端有一层过滤,客户端还有二次过滤。
服务端:
Tag 过滤使用的是 ConsumeQueue 的hashCode 与tag做对比(多个tag会挨个对比)
Properties 使用的是commitlog ,通过SqlFilter进行过滤。
MQ过滤原理
mq过滤总结
-
使用 CPU 资源来换取网卡流量资源
-
FilterServer 与 Broker 部署在同一台机器,数据通过本地回环通信,不走网卡
-
一台 Broker 部署多个 FilterServer,充分利用 CPU 资源,因为单个 Jvm 难以全面利用高配的物理机 Cpu 资源
-
因为过滤代码使用 Java 语言来编写,应用几乎可以做任意形式的服务器端消息过滤,例如通过 Message Header
进行过滤,甚至可以按照 Message Body 进行过滤。
-
使用 Java 语言进行作为过滤表达式是一个双刃剑,方便了应用的过滤操作,但是带来了服务器端的安全风险。
需要应用来保证过滤代码安全,例如在过滤程序里尽可能不做申请大内存,创建线程等操作。避免 Broker 服
务器发生资源泄漏。
SQL92过滤代码剖析
MQ主流程:
1:客户端启动,会在客户端缓存过滤表达式
consumer.subscribe("topic", "sql表达式");
。。。
this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);
2:在客户端启动后,与服务端建立心跳链接,服务端会注册该consumer的data filter如果是sql过滤,会准备好sqlFilter。对sql92的符号进行支持
3:客户端拉消息
3.1:先获取客户端缓存的过滤表达式,作为pullRequest入参,想服务端请求拉数据
3.2:获取messageFilter,sql过滤是通过commitLog内容进行解析
3.3:根据properties内容进行sqlFilter.parse解析对应的逻辑表达式的 expression实例,在通过expression对sql92符号进行逻辑运算(单独解释)
3.4:返回通过filter的message
通过官方单测,单独看下下主逻辑:
注册filter->获取parser->根据Properties map解析表达式
逻辑运算的代码实现大致思路:
1:通过解析,把支持的 and between等表达式解析为数学表达式( parser)
2:按(&& ||)切分为左右2个表达式,,两两逻辑匹配。支持短路
主流程逻辑代码如下
SQL表达式的主接口
构建sql filter data
isMatchedByCommitLog tag过滤会直接返回
Sql过滤会调用布尔表达式进行过滤