RocketMQ支持表达式过滤与类过滤两种模式,其中表达式又分为TAG和SQL92。类过滤模式允许提交一个过滤类到FilterServer,消息消费者从FilterServer拉取消息,消息经过FilterServer时会执行过滤逻辑。
基于表达式的消息过滤
消息发送者在消息发送时如果设置了消息的tags属性,存储在消息属性中,先存储在CommitLog文件中,然后转发到消息消费队列,消息消费队列会用8个字节存储消息tag的hashcode,之所以不直接存储tag字符串,是因为将ConumeQueue设计为定长结构,加快消息消费的加载性能。
RocketMQ基于表达式的消息过滤是在订阅时做过滤。在Broker端拉取消息时,遍历ConsumeQueue,只对比消息tag的hashcode,如果匹配则返回,否则忽略该消息。Consume在收到消息后,同样需要先对消息进行过滤,只是此时比较的是消息tag的值而不再是hashcode。
Step1:消费者订阅消息主题与消息过滤表达式。构建订阅信息subscriptionData并加入到RebalanceImpl进行消息队列负载。
subscriptionData的核心属性:
1)String SUB_ALL:过滤模式,默认为全匹配。
2)boolean classFilterMode:是否是类过滤模式,默认为false。
3)String topic:消息主题名称。
4)String subString:消息过滤表达式,多个用双竖线隔开,例如“TAGA||TAGB”。
5)Set<String> tagsSet:消息过滤tag集合,消费端过滤时进行消息过滤的依据。
6)Set<String> codeSet:消息过滤tag hashcode集合。
7)String expressionType:过滤类型,TAG或SQL92。
Step2:根据订阅消息构建消息拉取标记。根据主题、消息过滤表达式构建订阅消息实体。构建消息过滤对象。
Step3:根据偏移量拉取消息后,首先根据ConsumeQueue条目进行消息过滤,如果不匹配则直接跳过该条消息,继续拉取下一条消息。
Step4:如果消息根据ConsumeQueue条目通过过滤,则需要从CommitLog文件中加载整个消息体,然后根据属性进行过滤。基于TAG模式,根据ConsumeQueue进行消息过滤时只对比tag的hashcode,所以基于TAG模式消息过滤,还需要在消息消费端对消息tag进行精确匹配。
从消息拉取流程知道,消息拉取线程PullMessageService默认会使用异步方式从服务器拉取消息,如果消息过滤模式为TAG模式,并且订阅TAG集合不为空,则对消息的tag进行判断,如果集合中包含消息的TAG则返回给消费者消费,否则跳过。
消息过滤FilterServer
ClassFilter运行机制
基于类模式过滤是指在 Broker 端运行1个或多个消息过滤服务器(FilterServer), RocketMQ 允许消息消费者自定义消息过滤实现类并将其代码上传到 FilterServer 上,消息消费者向 FilterServer 拉取消息,FilterServer将消息消费者的拉取命令转发到 Broker,然后对返回的消息执行消息过滤逻辑,最终将消息返回给消费端。
1)Broker 进程所在的服务器会启动多个 FilterServer 进程。
2)消费者在订阅消息主题时会上传一个自定义的消息过滤实现类,FilterServer 加载并实例化
3)消息消费者(Consume)向 FilterServer 发送消息拉取请求,FilterServer 接收到消息消费者消息拉取请求后,FilterServer 将消息拉取请求转发给 Broker,Broker 返回消息后在 FilterServer 端执行消息过滤逻辑,然后返回符合订阅信息的消息给消息消费者进行消费。
FilterServer 注册
FilterServer在启动时会创建一个定时调度任务,每隔10s向Broker注册自己。
Step1:FilterServer从配置文件中获取Broker地址,然后将FilterServer所在机器的IP与监听端口发送到Broker服务器。
Step2:FilterServer与Broker通过心跳维持FilterServer在Broker端的注册,同样在Broker每隔10s扫描一下该注册表,如果30s内未收到FilterServer的注册信息,将关闭Broker与FilterServer的连接。Broker为了避免Broker端FilterServer的异常退出导致FilterServer进程越来越少,同样提供一个定时任务每30s检测一下当前存活的FilterServer进程个数。
经过上面的步骤,Broker上已经保存了FilterServer的信息。那么NameServer中关于Broker的filterServer信息是如何从消息服务器(Broker)传输到NameServer的呢?Broker通过与所有NameServer的心跳包向NameServer注册Broker上存储的FilterServer列表,指引消息消费者正确从FilterServer上拉取消息。Brokers每30s向所有NameServer发送心跳包,心跳包中包含了集群名称、Broker名称、Broker地址、BrokerId、haServer地址、topic配置、过滤服务器列表等。