Skip to content

订阅过滤器

EMQX 6.2 引入的订阅过滤器(Subscription Filter)功能在 MQTT 5.0 发布/订阅模型的基础上,增加了订阅层面的内容过滤能力。客户端可以只接收同时满足主题过滤器和过滤表达式的消息,从而减少不必要的消息投递,降低网络流量和资源消耗。

本页面对 EMQX 中的订阅过滤器功能进行完整介绍,涵盖设计动机、核心概念、过滤表达式语法、行为语义及实际应用场景。

什么是订阅过滤器?

订阅过滤器是附加在 MQTT 订阅上的可选过滤条件。当已发布的消息匹配订阅的主题过滤器时,EMQX 会在转发前根据消息的 MQTT 5.0 用户属性(User Properties)对过滤表达式进行求值。只有同时满足主题过滤器和过滤表达式的消息,才会被投递给订阅者。

标准 MQTT 消息路由将所有匹配主题的消息转发给订阅者:

Publisher --> Topic -- (Filter) --> Subscription --> Subscriber

订阅过滤器在消息路由路径中引入了第二级过滤:

Publisher --> Topic -- (Filter) --> Subscription -- (Filter) --> Subscriber

这一双层过滤机制同时支持基于主题和基于内容的过滤,使客户端能够精确指定希望接收的消息。

为什么使用订阅过滤器?

标准 MQTT 5.0 订阅仅根据主题匹配来路由消息,所有发布到匹配主题的消息都会被投递给每个订阅者,与消息内容无关。在以下场景中,这种方式存在局限性:

  • 订阅者只关注来自特定区域、设备组或类别的消息。
  • 高频主题承载着不同消费者需要独立拆分的混合数据。
  • 将所有消息下发给客户端会造成不必要的网络流量和处理负担。

订阅过滤器通过允许在订阅时声明精确的、基于内容的投递规则来解决上述问题,无需修改发布者、调整主题结构或为每个数据维度创建独立主题。

核心概念

  • 主题过滤器(Topic Filter):订阅中的标准 MQTT 主题过滤器部分(? 之前的内容),决定哪些消息进入路由阶段。

  • 过滤表达式(Filter Expression):基于内容的过滤条件(? 之后的内容),对通过主题过滤器的每条消息的 MQTT 5.0 用户属性进行求值。

  • 用户属性(User Properties):附加在 MQTT 5.0 消息上的键值对元数据。发布者可通过用户属性提供额外上下文信息(如 locationdevice_typeregion 等),供订阅者进行过滤。

  • 双阶段投递(Two-Stage Delivery):在消息投递给订阅者之前,依次对主题过滤器和过滤表达式进行求值的组合过程。

  • 无过滤订阅(Unfiltered Subscription):不含 ? 分隔符的订阅,与标准 MQTT 订阅行为一致,所有匹配主题的消息均会被投递,不受内容限制。

订阅过滤器的工作原理

订阅过滤器以 MQTT 5.0 用户属性作为过滤依据。当客户端发布消息时,可在消息的用户属性头部包含键值对。EMQX 对每个过滤表达式与这些键值对进行匹配,只有表达式满足时才投递消息。

订阅过滤器默认禁用。如需启用该功能,请参阅订阅过滤器快速入门

过滤语法

订阅过滤器通过 ? 作为分隔符附加在主题过滤器之后:

<topic-filter>?<filter-expression>
组成部分说明
<topic-filter>标准 MQTT 主题过滤器,例如 sensor/+/temperaturehome/#
?分隔主题过滤器与过滤表达式的分隔符
<filter-expression>针对消息用户属性进行求值的键值过滤条件

过滤表达式格式

过滤表达式支持相等匹配和比较运算符,多个条件通过 & 组合(逻辑与):

key1=value1&key2>value2
元素说明
key已发布消息中用户属性的键名
=相等匹配(键的值必须等于指定字符串)
>数值比较(键的值必须大于指定数字)
&组合多个条件,所有条件均为真时消息才会被投递

过滤表达式区分大小写。若消息的用户属性中不存在指定的键,该消息将被过滤掉。

TIP

订阅过滤器仅适用于 MQTT 5.0 客户端。当该功能启用时,使用含 ? 主题字符串进行订阅的 MQTT 3.1.1 客户端,其 ? 及后续内容将被视为主题过滤器的字面字符。

行为语义

  • 只有当主题过滤器匹配过滤表达式求值为真时,EMQX 才会将消息投递给订阅者。
  • 若过滤表达式引用的键在消息用户属性中不存在,该消息不会被投递给该订阅者。
  • 每个订阅的过滤表达式独立求值,一条消息是否投递给某个订阅者,不影响其是否被投递给同主题上的其他订阅者。
  • 不含 ? 分隔符的订阅与标准 MQTT 订阅行为完全一致。
  • 过滤表达式在服务端求值,客户端无需承担任何过滤逻辑。

过滤表达式示例

以下示例展示了常见的订阅模式:

订阅字符串含义
sensor/+/temperature?location=roomA接收用户属性中包含 location=roomA 的温度消息。
sensor/+/temperature?value>25接收用户属性中 value 大于 25 的温度消息。
sensor/+/temperature?location=roomA&unit=celsius接收同时满足 location=roomAunit=celsius 的温度消息。
home/lights/#标准订阅,投递所有匹配主题的消息。

发布者

发布者向 sensor/1/temperature 发布一条消息,用户属性如下:

json
{
  "location": "roomA",
  "unit": "celsius"
}

订阅者

订阅是否投递?原因
sensor/+/temperature?location=roomAlocation=roomA 匹配
sensor/+/temperature?location=roomBlocation 值不匹配
sensor/+/temperature?location=roomA&unit=celsius两个条件均匹配
sensor/+/temperature?location=roomA&unit=fahrenheitunit 值不匹配
sensor/+/temperature无过滤表达式,标准订阅

授权注意事项

启用授权后,EMQX 根据配置的规则校验订阅主题。用于授权的主题为基础主题过滤器? 分隔符之前的部分),过滤表达式在授权求值前会被剥离。

例如,订阅 sensor/+/temperature?location=roomA 的客户端,授权时使用的主题为 sensor/+/temperature。请确保授权规则中已覆盖与订阅过滤器配合使用的基础主题模式。

相关功能

订阅过滤器与 EMQX 中其他消息功能相辅相成:

  • 共享订阅:在订阅者组之间分发消息以实现负载均衡,不支持基于内容的过滤。
  • 保留消息:为新订阅者存储每个主题的最后一条消息,保留消息的投递不受订阅过滤表达式影响。
  • 主题重写:在路由前对主题字符串进行重写,主题重写规则在订阅过滤器求值之前执行。
  • 通配符订阅:使用 +# 通配符匹配多个主题,通配符主题过滤器可与订阅过滤器组合使用。
  • 消息队列:提供持久化的异步消息队列能力,支持持久存储和可配置的分发策略。

下一步

了解订阅过滤器的概念后,可通过以下内容进行实践: