规则引擎

EMQ X Rule Engine 提供了配置式的消息和设备事件处理规则,简化业务开发流程并降低与EMQ X的耦合度。规则引擎在消息发布或事件触发时执行SQL语句,支持事件监听、数据筛选、消息存储、转发等功能,广泛应用于物联网场景,如设备状态记录、消息存储、消息路由等。用户可以通过Dashboard进行SQL语法测试,确保规则引擎的正确配置和使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 规则引擎

EMQ X Rule Engine (以下简称规则引擎) 用于配置 EMQ X 消息流与设备事件的处理、响应规则。规则引擎不仅提供了清晰、灵活的 "配置式" 的业务集成方案,简化了业务开发流程,提升用户易用性,降低业务系统与 EMQ X 的耦合度;也为 EMQ X 的私有功能定制提供了一个更优秀的基础架构。

![rule-engine.jpg](http://dgiot-1253666439.cos.ap-shanghai-fsi.myqcloud.com/shuwa_tech/zh/backend/dgiot/rule-engine/rule-engine.jpg)


EMQ X 在 **消息发布或事件触发** 时将触发规则引擎,满足触发条件的规则将执行各自的 SQL 语句筛选并处理消息和事件的上下文信息。

## EMQ X 规则引擎快速入门

此处包含规则引擎的简介与实战,演示使用规则引擎结合华为云 RDS 上的 MySQL 服务,进行物联网 MQTT 设备在线状态记录、消息存储入库。

从本视频中可以快速了解规则引擎解决的问题和基础使用方法。

<iframe style="width: 500px; height: 360px; margin: 10px auto" src="//player.bilibili.com/player.html?aid=927036281&bvid=BV19T4y1w7Nj&cid=233977851&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>

## 消息发布

规则引擎借助响应动作可将特定主题的消息处理结果存储到数据库,发送到 HTTP Server,转发到消息队列 Kafka 或 RabbitMQ,重新发布到新的主题甚至是另一个 Broker 集群中,每个规则可以配置多个响应动作。

选择发布到 t/# 主题的消息,并筛选出全部字段:

```sql
SELECT * FROM "t/#"
```

选择发布到 t/a 主题的消息,并从 JSON 格式的消息内容中筛选出 "x" 字段:

```sql
SELECT payload.x as x FROM "t/a"
```

## 事件触发

规则引擎使用 **$events/** 开头的虚拟主题(**事件主题**)处理 EMQ X 内置事件,内置事件提供更精细的消息控制和客户端动作处理能力,可用在 QoS 1 QoS 2 的消息抵达记录、设备上下线记录等业务中。

选择客户端连接事件,筛选 Username 为 'emqx' 的设备并获取连接信息:

```sql
SELECT clientid, connected_at FROM "$events/client_connected" WHERE username = 'emqx'
```

规则引擎数据和 SQL 语句格式,[事件主题](#event-topics) 列表详细教程参见 [SQL 手册](#rule-sql)。

## 最小规则

规则描述了 **数据从哪里来**、**如何筛选并处理数据**、**处理结果到哪里去** 三个配置,即一条可用的规则包含三个要素:

- 触发事件:规则通过事件触发,触发时事件给规则注入事件的上下文信息(数据源),通过 SQL 的 FROM 子句指定事件类型;
- 处理规则(SQL):使用 SELECT 子句 和 WHERE 子句以及内置处理函数, 从上下文信息中过滤和处理数据;
- 响应动作:如果有处理结果输出,规则将执行相应的动作,如持久化到数据库、重新发布处理后的消息、转发消息到消息队列等。一条规则可以配置多个响应动作。

如图所示是一条简单的规则,该条规则用于处理 **消息发布** 时的数据,将全部主题消息的 `msg` 字段,消息 `topic` 、`qos` 筛选出来,发送到 Web Server 与 /uplink 主题:

![dashboard.png](http://dgiot-1253666439.cos.ap-shanghai-fsi.myqcloud.com/shuwa_tech/zh/backend/dgiot/rule-engine/dashboard.png)

## 规则引擎典型应用场景举例

- 动作监听:智慧家庭智能门锁开发中,门锁会因为网络、电源故障、人为破坏等原因离线导致功能异常,使用规则引擎配置监听离线事件向应用服务推送该故障信息,可以在接入层实现第一时间的故障检测的能力;
- 数据筛选:车辆网的卡车车队管理,车辆传感器采集并上报了大量运行数据,应用平台仅关注车速大于 40 km/h 时的数据,此场景下可以使用规则引擎对消息进行条件过滤,向业务消息队列写入满足条件的数据;
- 消息路由:智能计费应用中,终端设备通过不同主题区分业务类型,可通过配置规则引擎将计费业务的消息接入计费消息队列并在消息抵达设备端后发送确认通知到业务系统,非计费信息接入其他消息队列,实现业务消息路由配置;
- 消息编解码:其他公共协议 / 私有 TCP 协议接入、工控行业等应用场景下,可以通过规则引擎的本地处理函数(可在 EMQ X 上定制开发)做二进制 / 特殊格式消息体的编解码工作;亦可通过规则引擎的消息路由将相关消息流向外部计算资源如函数计算进行处理(可由用户自行开发处理逻辑),将消息转为业务易于处理的 JSON 格式,简化项目集成难度、提升应用快速开发交付能力。

## 迁移指南

4.0 版本中规则引擎 SQL 语法更加易用,3.x 版本中所有事件 **FROM** 子句后面均需要指定事件名称,4.0 以后我们引入 **事件主题** 概念,默认情况下 **消息发布** 事件不再需要指定事件名称:

```sql
## 3.x 版本
## 需要指定事件名称进行处理
SELECT * FROM "t/#" WHERE topic =~ 't/#'

## 4.0 及以后版本
## 默认处理 message.publish 事件,FROM 后面直接填写 MQTT 主题
## 上述 SQL 语句等价于:
SELECT * FROM 't/#'

## 其他事件,FROM 后面填写事件主题
SELECT * FROM "$events/message_acked" where topic =~ 't/#'
SELECT * FROM "$events/client_connected"
```

::: tip
Dashboard 中提供了旧版 SQL 语法转换功能可以完成 SQL 升级迁移。
:::

## 规则引擎组成

使用 EMQ X 的规则引擎可以灵活地处理消息和事件。使用规则引擎可以方便地实现诸如将消息转换成指定格式,然后存入数据库表,或者发送到消息队列等。

与 EMQ X 规则引擎相关的概念包括: 规则(rule)、动作(action)、资源(resource) 和 资源类型(resource-type)。

规则、动作、资源的关系:
```
规则: {
    SQL 语句,
    动作列表: [
        {
            动作1,
            动作参数,
            绑定资源: {
                资源配置
            }
        },
        {
            动作2,
            动作参数,
            绑定资源: {
                资源配置
            }
         }
    ]
}
```
- 规则(Rule): 规则由 SQL 语句和动作列表组成。动作列表包含一个或多个动作及其参数。
- SQL 语句用于筛选或转换消息中的数据。
- 动作(Action) 是 SQL 语句匹配通过之后,所执行的任务。动作定义了一个针对数据的操作。
  动作可以绑定资源,也可以不绑定。例如,“inspect” 动作不需要绑定资源,它只是简单打印数据内容和动作参数。而 “data_to_webserver” 动作需要绑定一个 web_hook 类型的资源,此资源中配置了 URL。
- 资源(Resource): 资源是通过资源类型为模板实例化出来的对象,保存了与资源相关的配置(比如数据库连接地址和端口、用户名和密码等) 和系统资源(如文件句柄,连接套接字等)。
- 资源类型 (Resource Type): 资源类型是资源的静态定义,描述了此类型资源需要的配置项。

::: tip
动作和资源类型是由 emqx 或插件的代码提供的,不能通过 API 和 CLI 动态创建。
:::

## SQL 语句
### SQL 语法
**FROM、SELECT 和 WHERE 子句:**

规则引擎的 SQL 语句基本格式为:

```sql
SELECT <字段名> FROM <主题> [WHERE <条件>]
```

- `FROM` 子句将规则挂载到某个主题上
- `SELECT` 子句用于对数据进行变换,并选择出感兴趣的字段
- `WHERE` 子句用于对 SELECT 选择出来的某个字段施加条件过滤

**FOREACH、DO 和 INCASE 子句:**

如果对于一个数组数据,想针对数组中的每个元素分别执行一些操作并执行 Actions,需要使用 `FOREACH-DO-INCASE` 语法。其基本格式为:

```sql
FOREACH <字段名> [DO <条件>] [INCASE <条件>] FROM <主题> [WHERE <条件>]
```

- `FOREACH` 子句用于选择需要做 foreach 操作的字段,注意选择出的字段必须为数组类型
- `DO` 子句用于对 FOREACH 选择出来的数组中的每个元素进行变换,并选择出感兴趣的字段
- `INCASE` 子句用于对 DO 选择出来的某个字段施加条件过滤

其中 DO 和 INCASE 子句都是可选的。DO 相当于针对当前循环中对象的 SELECT 子句,而 INCASE 相当于针对当前循环中对象的 WHERE 语句。

### 事件和事件主题
规则引擎的 SQL 语句既可以处理消息(消息发布),也可以处理事件(客户端上下线、客户端订阅等)。对于消息,FROM 子句后面直接跟主题名;对于事件,FROM 子句后面跟事件主题。

事件消息的主题以 `"$events/"` 开头,比如 `"$events/client_connected",` `"$events/session_subscribed"。`
如果想让 emqx 将事件消息发布出来,可以在 `emqx_rule_engine.conf` 文件中配置。

所有支持的事件及其可用字段详见: [规则事件](#rule-sql-events)。

### SQL 语句示例:
#### 基本语法举例

- 从 topic 为 "t/a" 的消息中提取所有字段:

```sql
SELECT * FROM "t/a"
```

- 从 topic 为 "t/a" 或 "t/b" 的消息中提取所有字段:

```sql
SELECT * FROM "t/a","t/b"
```

- 从 topic 能够匹配到 't/#' 的消息中提取所有字段。

```sql
SELECT * FROM "t/#"
```

- 从 topic 能够匹配到 't/#' 的消息中提取 qos, username 和 clientid 字段:

```sql
SELECT qos, username, clientid FROM "t/#"
```

- 从任意 topic 的消息中提取 username 字段,并且筛选条件为 username = 'Steven':

```sql
SELECT username FROM "#" WHERE username='Steven'
```

- 从任意 topic 的 JSON 消息体(payload) 中提取 x 字段,并创建别名 x 以便在 WHERE 子句中使用。WHERE 子句限定条件为 x = 1。下面这个 SQL 语句可以匹配到消息体 {"x": 1}, 但不能匹配到消息体 {"x": 2}:

```sql
SELECT payload as p FROM "#" WHERE p.x = 1
```

- 类似于上面的 SQL 语句,但嵌套地提取消息体中的数据,下面的 SQL 语句可以匹配到 JSON 消息体 {"x": {"y": 1}}:

```sql
SELECT payload as a FROM "#" WHERE a.x.y = 1
```

- 在 clientid = 'c1' 尝试连接时,提取其来源 IP 地址和端口号:

```sql
SELECT peername as ip_port FROM "$events/client_connected" WHERE clientid = 'c1'
```

- 筛选所有订阅 't/#' 主题且订阅级别为 QoS1 的 clientid:

```sql
SELECT clientid FROM "$events/session_subscribed" WHERE topic = 't/#' and qos = 1
```

- 筛选所有订阅主题能匹配到 't/#' 且订阅级别为 QoS1 的 clientid。注意与上例不同的是,这里用的是主题匹配操作符 **'=~'**,所以会匹配订阅 't' 或 't/+/a' 的订阅事件:

```sql
SELECT clientid FROM "$events/session_subscribed" WHERE topic =~ 't/#' and qos = 1
```

::: tip

- FROM 子句后面的主题需要用双引号 `""` 引起来。
- WHERE 子句后面接筛选条件,如果使用到字符串需要用单引号 `''` 引起来。
- FROM 子句里如有多个主题,需要用逗号 `","` 分隔。例如 SELECT * FROM "t/1", "t/2" 。
- 可以使用使用 `"."` 符号对 payload 进行嵌套选择。

:::

#### 遍历语法(FOREACH-DO-INCASE) 举例

假设有 ClientID 为 `c_steve`、主题为 `t/1` 的消息,消息体为 JSON 格式,其中 sensors 字段为包含多个 Object 的数组:

```json
{
    "date": "2020-04-24",
    "sensors": [
        {"name": "a", "idx":0},
        {"name": "b", "idx":1},
        {"name": "c", "idx":2}
    ]
}
```

**示例1: 要求将 sensors 里的各个对象,分别作为数据输入重新发布消息到 `sensors/${idx}` 主题,内容为 `${name}`。即最终规则引擎将会发出 3 条消息:**

1) 主题:sensors/0
   内容:a
2) 主题:sensors/1
   内容:b
3) 主题:sensors/2
   内容:c

要完成这个规则,我们需要配置如下动作:

- 动作类型:消息重新发布 (republish)
- 目的主题:sensors/${idx}
- 目的 QoS:0
- 消息内容模板:${name}

以及如下 SQL 语句:

```sql
FOREACH
    payload.sensors
FROM "t/#"
```

**示例解析:**

这个 SQL 中,FOREACH 子句指定需要进行遍历的数组 sensors,则选取结果为:

```json
[
  {
    "name": "a",
    "idx": 0
  },
  {
    "name": "b",
    "idx": 1
  },
  {
    "name": "c",
    "idx": 2
  }
]
```

FOREACH 语句将会对于结果数组里的每个对象分别执行 "消息重新发布" 动作,所以将会执行重新发布动作 3 次。

**示例2: 要求将 sensors 里的 `idx` 值大于或等于 1 的对象,分别作为数据输入重新发布消息到 `sensors/${idx}` 主题,内容为 `clientid=${clientid},name=${name},date=${date}`。即最终规则引擎将会发出 2 条消息:**

1) 主题:sensors/1
   内容:clientid=c_steve,name=b,date=2020-04-24
2) 主题:sensors/2
   内容:clientid=c_steve,name=c,date=2020-04-24

要完成这个规则,我们需要配置如下动作:

- 动作类型:消息重新发布 (republish)
- 目的主题:sensors/${idx}
- 目的 QoS:0
- 消息内容模板:clientid=${clientid},name=${name},date=${date}

以及如下 SQL 语句:

```sql
FOREACH
    payload.sensors
DO
    clientid,
    item.name as name,
    item.idx as idx
INCASE
    item.idx >= 1
FROM "t/#"
```

**示例解析:**

这个 SQL 中,FOREACH 子句指定需要进行遍历的数组 `sensors`; DO 子句选取每次操作需要的字段,这里我们选了外层的 `clientid` 字段,以及当前 sensor 对象的 `name` 和 `idx` 两个字段,注意 `item` 代表 sensors 数组中本次循环的对象。INCASE 子句是针对 DO 语句中字段的筛选条件,仅仅当 idx >= 1 满足条件。所以 SQL 的选取结果为:

```json
[
  {
    "name": "b",
    "idx": 1,
    "clientid": "c_emqx"
  },
  {
    "name": "c",
    "idx": 2,
    "clientid": "c_emqx"
  }
]
```

FOREACH 语句将会对于结果数组里的每个对象分别执行 "消息重新发布" 动作,所以将会执行重新发布动作 2 次。

在 DO 和 INCASE 语句里,可以使用 `item` 访问当前循环的对象,也可以通过在 FOREACH 使用 `as` 语法自定义一个变量名。所以本例中的 SQL 语句又可以写为:

```sql
FOREACH
    payload.sensors as s
DO
    clientid,
    s.name as name,
    s.idx as idx
INCASE
    s.idx >= 1
FROM "t/#"
```

**示例3: 在示例2 的基础上,去掉 clientid 字段 `c_steve` 中的 `c_` 前缀*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值