Skip to content

Latest commit

 

History

History
480 lines (381 loc) · 15.9 KB

README.md

File metadata and controls

480 lines (381 loc) · 15.9 KB

项目代码和文档

一、整体架构

Bifromq 规则引擎设计整体框架如下:

imag

整体来看,整个框架设计实现一个可扩展、分布式的规则引擎,其中包含了数据存储、消息传递、规则处理和系统管理等多个组件。每个组件都有其特定的职责,并通过定义良好的接口和协议相互协作。其中主要组件如下:

  • 管理节点:

负责管理和协调其他节点的中心节点,负责规则的分发、任务的调度监控、提供API接口。包括Restful API 和 协调器俩个子组件。

  • 工作节点:

工作节点是从管理节点接收规则、执行规则、上报状态等服务实例,同时根据规则订阅Bifromq MQTT Broker上的消息,处理消息。包含协调器代理 和 规则执行器俩个子组件。

  • 控制台:

提供了一个图形用户界面,允许管理员或用户配置数据源、规则、查看规则执行状态和处理结果等功能。

  • MySQL:

作为持久化存储,用于存储用户和权限,工作节点状态、数据源、规则定义和执行状态等数据。

  • Sources/Sinks:

支持Sources/Sinks 包括Bifromq、kafka、redis、memory。 提供规则处理的数据来源和规则处理结果输出存储。

二、协调器工作原理

协调器工作原理如下图:

imag

协调器在这个系统中扮演着核心角色,类似于大脑,负责节点管理、任务调度、状态监控和通信协调。以下是协调器及其代理组件的详细工作流程:

协调器组件:

  1. 节点健康检查协程:定期对工作节点进行健康检查,确保它们正常运行。不健康的节点将从调度队列中移除,以保证任务分配的高效性。
  2. 规则任务执行状态检查协程:通过协调器代理的Restful API,定期检查工作节点上规则任务的执行状态,确保任务按照预期进行。
  3. 规则任务调度协程:根据规则任务的定义和触发条件,智能地在适当的工作节点上调度任务执行。
  4. Restful API协程:提供Restful API接口,实现与工作节点上的协调器代理的通信。主要负责下发全局配置和数据源配置、节点的注册与注销、节点健康状态的上报等。

协调器代理组件:

  1. 节点启动:在节点启动时,通过协调器的Restful API获取全局启动配置和数据源配置,同时完成节点的注册,并启动健康状态上报和数据更新协程。
  2. 健康状态上报和数据更新协程:负责将节点的健康状态上报给协调器。同时,如果管理节点上的数据源配置有更新,将同步更新工作节点的状态和数据源配置。
  3. Restful API协程:提供Restful API接口,与协调器进行通信,负责提交规则任务、查询规则任务的执行状态,以及控制规则任务的启动、停止和删除。

通过这些组件和流程,协调器确保了系统的高效运行和灵活的任务管理,同时也提供了强大的监控和控制能力。

三、 规则设计

规则整体结构如下图:

imag

规则集(RuleSet) 规则集能够定义复杂的事件处理逻辑,从数据的接收、处理到最终的响应,形成一个完整的数据处理流程。一个规则集包括 多条Stream(流) 和 多条Rule(规则)组成。

Stream(流)

流定义需要指定其数据源类型以定义与外部资源的连接方式。数据源作为流使用时,源必须为无界的。在规则中,流的行为类似事件触发器。每个事件都会触发规则的一次计算。

流定义如下:

   CREATE STREAM
   stream_name
   ( column_name <data_type> [ ,...n ] )
   WITH ( property_name = expression [, ...] );

流定义是一个 SQL 语句。它由两部分组成。

  1. 流的模式定义。其语法与 SQL 表定义相同。这里的模式是可选的。如果它是空的,则流是无模式的。
  2. 在 WITH 子句中定义连接器类型和行为的属性,如序列化格式。
CREATE STREAM demo (
        USERID BIGINT,
        FIRST_NAME STRING,
        LAST_NAME STRING,
        NICKNAMES ARRAY(STRING),
        Gender BOOLEAN,
        ADDRESS STRUCT(STREET_NAME STRING, NUMBER BIGINT),
    ) WITH (DATASOURCE="test/", FORMAT="JSON", KEY="USERID", CONF_KEY="demo");
create stream demo (device string, temperature float, humidity float) WITH (FORMAT="JSON", DATASOURCE="devices/+/messages")

Rule(规则)

每条规则都代表了运行的一项计算工作。它定义了连续流数据源作为输入,计算逻辑和结果 sink 作为输出。

1. SQL 规则

SQL 规则 通过指定 sql 和 actions 属性,sql 定义了针对预定义流运行的 SQL 查询,这将转换数据。然后,输出的数据可以通过 action 路由到多个位置。

{
   "id": "rule1",
   "sql": "select * from demo where temperature > 10",
   "actions": [
     {
       "log": {}
     },
     {
       "mqtt": {
         "server": "tcp://bifromq-server:1883",
         "topic": "devices/allmessages"
       }
     },
     {
       "kafka":{
         "brokers": "kafka-server:9092",
         "topic": "allmessages",
         "saslAuthType": "none"
       }
     }
   ]
}

2. 图规则

graph 属性是有向无环图的 JSON 表述。它由 nodes 和 topo 组成,分别定义了图中的节点和它们的边。下面是一个由图形定义的最简单的规则。它定义了3个节点:demo,humidityFilter 和 mqttOut。这个图是线性的,即demo->humidityFilter->mqttOut。该规则将从mqtt(demo)读取,通过湿度过滤(humidityFilter)并汇入mqtt(mqttOut)。

{
  "id": "rule1",
  "name": "Test Condition",
  "graph": {
    "nodes": {
      "demo": {
        "type": "source",
        "nodeType": "mqtt",
        "props": {
          "datasource": "devices/+/messages"
        }
      },
      "humidityFilter": {
        "type": "operator",
        "nodeType": "filter",
        "props": {
          "expr": "humidity > 30"
        }
      },
      "mqttout": {
        "type": "sink",
        "nodeType": "mqtt",
        "props": {
          "server": "tcp://bifromq-server:1883",
          "topic": "devices/result"
        }
      }
    },
    "topo": {
      "sources": ["demo"],
      "edges": {
        "demo": ["humidityFilter"],
        "humidityFilter": ["mqttout"]
      }
    }
  }
}

四、函数 Function

支持函数如下图:

imag

五、数据连接

支持 Source 和 Sink 如下图:

imag

六、执行器

执行器是基于开源 eKuiper 二次开发进行构建,eKuiper 是 Golang 实现的轻量级物联网边缘分析、流式处理开源软件,可以运行在各类资源受限的边缘设备上。eKuiper 的规则引擎是基于 SQL 或基于图形(类似于 Node-RED)的规则。

imag

eKuiper 通过规则/SQL 解析器或图规则解析器将解析、规划和优化规则,使其成为一系列算子的流程,算子可以利用流式运行时和状态存储。算子之间通过 Go 通道进行异步通信。 受益于 Go 的并发模型,规则运行时可以做到:

  • 以异步和非阻塞的方式进行通信。
  • 充分利用多核计算。
  • 算子层可伸缩。
  • 规则之间相互隔离。

在 eKuiper 中,计算工作以规则的形式呈现。规则以流的数据源为输入,通过 SQL 定义计算逻辑,将结果输出到动作/sink 中。 规则定义提交后,它将持续运行。它将不断从源获取数据,根据 SQL 逻辑进行计算,并根据结果触发行动。核心计算组件包括 Rule、Source、Sink。

七、部署

在项目 deploy/docker 目录下有个 docker-compose.yaml 文件,内容如下:

version: '3.9'
networks:
  bifromq-net:
    external: false
services:
  db:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/mysql:5.7.34  # 使用MySQL 5.7镜像,你可以选择其他版本
    networks:
      - bifromq-net
    ports:
      - "3308:3306/tcp"
    volumes:
      - ./data/mysql:/var/lib/mysql  # 将数据库文件存储在卷中,以便持久化存储
      - ./data/init:/docker-entrypoint-initdb.d
    restart: always  # 容器退出时总是重启
    environment:
      MYSQL_ROOT_PASSWORD: 123456  # 设置root用户的密码
      MYSQL_DATABASE: engine  # 创建并初始化一个数据库
      MYSQL_USER: dev  # 创建一个新用户
      MYSQL_PASSWORD: 123456  # 设置新用户的密码
  bifromq_engine:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/bifromq_engine:v1.0.3
    command: ["serve", "--api-port=8090","--coordinator-port=8091","--dns=root:123456@tcp(db:3306)/engine?charset=utf8mb4&parseTime=True&loc=Local"]
    environment:
      - JWT_SIGNING_KEY=bifromq
    networks:
      - bifromq-net
    ports:
      - "8090:8090/tcp"
      - "8091:8091/tcp"
    restart: always
    depends_on:
      - db
  bifromq-server:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/bifromq:latest
    environment:
      - JVM_HEAP_OPTS=-Xms1G -Xmx2G
    networks:
      - bifromq-net
    ports:
      - "1883:1883/tcp"
    restart: always

  redis-server:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/redis:latest  # 使用最新版本的Redis镜像
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
    networks:
      - bifromq-net
    volumes:
      - ./data/redis:/bitnami/redis  # 持久化Redis数据
    ports:
      - "6379:6379"  # 将容器的6379端口映射到宿主机的6379端口
    restart: always  # 容器退出时总是重启
  zookeeper:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/zookeeper:3.8
    networks:
      - bifromq-net
    ports:
      - "2181:2181"
    volumes:
      - ./data/zookeeper:/bitnami
    environment:
      - ALLOW_ANONYMOUS_LOGIN=yes
  kafka-server:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/kafka:3.4
    networks:
      - bifromq-net
    ports:
      - "9092:9092"
    volumes:
      - ./data/kafka:/bitnami
    environment:
      - KAFKA_CFG_ZOOKEEPER_CONNECT=zookeeper:2181
      - ALLOW_PLAINTEXT_LISTENER=yes
      - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092
      - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka-server:9092
      - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
    depends_on:
      - zookeeper
  bifromq-rule-engine-joba:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/ekuiperd:v1.0.0
    environment:
      - NODE_IP=bifromq-rule-engine-joba
      - NODE_PORT=9082
      - NODE_NAME=bifromq-rule-engine-joba
      - NODE_TAG=job,joba
      - COORDINATOR_HOST=bifromq_engine:8091
    networks:
      - bifromq-net
    restart: always
    depends_on:
      - db
      - bifromq_engine
  bifromq-rule-engine-jobb:
    image: registry.cn-hangzhou.aliyuncs.com/2456868764/ekuiperd:v1.0.0
    environment:
      - NODE_IP=bifromq-rule-engine-jobb
      - NODE_PORT=9082
      - NODE_NAME=bifromq-rule-engine-jobb
      - NODE_TAG=job,jobb
      - COORDINATOR_HOST=bifromq_engine:8091
    networks:
      - bifromq-net
    restart: always
    depends_on:
      - db
      - bifromq_engine

启动命令:

docker compose up -d

启动 包括 数据库mysql db, 管理节点 bifromq engine, 控制台 bifromq ui, bifromq broker server, redis server , kafka server, 工作节点 A :rule engine job a ,工作节点 B: rule engine job b 容器。

imag

访问 http://127.0.0.1/8090 进入 bifromq 控制台管理界面, 账号和密码 admin/admin。

imag

七、控制台

imag imag imag imag

八、案例

1. Demo Sinks

#1 创建源流

CREAT STREAM demo (device string, temperature float, humidity float) WITH (FORMAT="JSON", DATASOURCE="devices/+/messages")

#2 创建规则和目标
{
  "id": "demo",
  "sql": "select * from demo where temperature > 10",
  "actions": [
    {
      "log": {}
    },
    {
      "mqtt": {
        "server": "tcp://bifromq-server:1883",
        "topic": "devices/allmessages"
      }
    },
    {
      "kafka":{
        "brokers": "kafka-server:9092",
        "topic": "test",
        "saslAuthType": "none"
      }
    }
  ]
}

测试结果: imag

2. 规则管道 - pipeline

以通过将先前规则的结果导入后续规则来形成规则管道。 这可以通过使用中间存储或 MQ(例如 mqtt 消息服务器)来实现。 通过同时使用 内存源 和 目标,我们可以创建没有外部依赖的规则管道。

imag

#1 创建源流

CREAT STREAM pipeline (device string, temperature float, humidity float) WITH (DATASOURCE="devices/pipeline", FORMAT="JSON")"


#2 创建规则和内存目标
{
  "id": "pipeline-rule1",
  "sql": "SELECT * FROM pipeline WHERE isNull(temperature)=false",
  "actions": [
    {
      "log": {}
    },
    {
      "memory": {
        "topic": "devices/ch1/sensor1"
      }
    }
  ]
}
#3 从内存主题创建一个流

CREATE STREAM sensor1 (temperature FLOAT, humidity FLOAT) 
WITH (DATASOURCE="devices/+/sensor1", FORMAT="JSON", TYPE="memory")

#4 从内存主题创建另一个要使用的规则
{
  "id": "rule2-1",
  "sql": "SELECT avg(temperature) FROM sensor1 GROUP BY CountWindow(10)",
  "actions": [
    {
      "log": {}
    },
    {
      "memory": {
        "topic": "analytic/sensors"
      }
    }
  ]
}

{
  "id": "rule2-2",
  "sql": "SELECT temperature + 273.15 as k FROM sensor1",
  "actions": [
    {
      "log": {}
    }
  ]
}

测试结果:

time="2024-06-29T09:45:52+08:00" level=info msg="sink result for rule pipeline-rule1: [{\"device\":\"sensor2\",\"humidity\":11.12,\"temperature\":12.1}]" file="sink/log_sink.go:32" rule=pipeline-rule1
time="2024-06-29T09:45:52+08:00" level=info msg="sink result for rule rule2-2: [{\"k\":285.25}]" file="sink/log_sink.go:32" rule=rule2-2
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule pipeline-rule1: [{\"device\":\"sensor2\",\"humidity\":11.12,\"temperature\":12.1}]" file="sink/log_sink.go:32" rule=pipeline-rule1
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule pipeline-rule1: [{\"device\":\"sensor2\",\"humidity\":11.12,\"temperature\":12.1}]" file="sink/log_sink.go:32" rule=pipeline-rule1
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule rule2-2: [{\"k\":285.25}]" file="sink/log_sink.go:32" rule=rule2-2
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule rule2-2: [{\"k\":285.25}]" file="sink/log_sink.go:32" rule=rule2-2
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule rule2-2: [{\"k\":285.25}]" file="sink/log_sink.go:32" rule=rule2-2
time="2024-06-29T09:45:53+08:00" level=info msg="sink result for rule pipeline-rule1: [{\"device\":\"sensor2\",\"humidity\":11.12,\"temperature\":12.1}]" file="sink/log_sink.go:32" rule=pipeline-rule1

九 项目构建

prebuild

build 项目之前,需要下载项目 ekuiper 特定分支到本地 external 目录 执行 make prebuild

make prebuild

Reference