订阅过滤器
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 消息上的键值对元数据。发布者可通过用户属性提供额外上下文信息(如
location、device_type、region等),供订阅者进行过滤。双阶段投递(Two-Stage Delivery):在消息投递给订阅者之前,依次对主题过滤器和过滤表达式进行求值的组合过程。
无过滤订阅(Unfiltered Subscription):不含
?分隔符的订阅,与标准 MQTT 订阅行为一致,所有匹配主题的消息均会被投递,不受内容限制。
订阅过滤器的工作原理
订阅过滤器以 MQTT 5.0 用户属性作为过滤依据。当客户端发布消息时,可在消息的用户属性头部包含键值对。EMQX 对每个过滤表达式与这些键值对进行匹配,只有表达式满足时才投递消息。
订阅过滤器默认禁用。如需启用该功能,请参阅订阅过滤器快速入门。
过滤语法
订阅过滤器通过 ? 作为分隔符附加在主题过滤器之后:
<topic-filter>?<filter-expression>| 组成部分 | 说明 |
|---|---|
<topic-filter> | 标准 MQTT 主题过滤器,例如 sensor/+/temperature 或 home/# |
? | 分隔主题过滤器与过滤表达式的分隔符 |
<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=roomA 和 unit=celsius 的温度消息。 |
home/lights/# | 标准订阅,投递所有匹配主题的消息。 |
发布者
发布者向 sensor/1/temperature 发布一条消息,用户属性如下:
{
"location": "roomA",
"unit": "celsius"
}订阅者
| 订阅 | 是否投递? | 原因 |
|---|---|---|
sensor/+/temperature?location=roomA | 是 | location=roomA 匹配 |
sensor/+/temperature?location=roomB | 否 | location 值不匹配 |
sensor/+/temperature?location=roomA&unit=celsius | 是 | 两个条件均匹配 |
sensor/+/temperature?location=roomA&unit=fahrenheit | 否 | unit 值不匹配 |
sensor/+/temperature | 是 | 无过滤表达式,标准订阅 |
授权注意事项
启用授权后,EMQX 根据配置的规则校验订阅主题。用于授权的主题为基础主题过滤器(? 分隔符之前的部分),过滤表达式在授权求值前会被剥离。
例如,订阅 sensor/+/temperature?location=roomA 的客户端,授权时使用的主题为 sensor/+/temperature。请确保授权规则中已覆盖与订阅过滤器配合使用的基础主题模式。
相关功能
订阅过滤器与 EMQX 中其他消息功能相辅相成:
- 共享订阅:在订阅者组之间分发消息以实现负载均衡,不支持基于内容的过滤。
- 保留消息:为新订阅者存储每个主题的最后一条消息,保留消息的投递不受订阅过滤表达式影响。
- 主题重写:在路由前对主题字符串进行重写,主题重写规则在订阅过滤器求值之前执行。
- 通配符订阅:使用
+和#通配符匹配多个主题,通配符主题过滤器可与订阅过滤器组合使用。 - 消息队列:提供持久化的异步消息队列能力,支持持久存储和可配置的分发策略。
下一步
了解订阅过滤器的概念后,可通过以下内容进行实践:
- 订阅过滤器快速开始:启用该功能,并按照分步指南使用 MQTTX CLI 端对端验证过滤行为。