iot接入平台落地方案

概述

目前设计的物联网平台(以下简称平台)主要负责以下事宜:

  1. 设备管理。设备模型抽象、相关数据维护;
  2. 设备接入。即所有设备的连接工作;
  3. 设备事件上报。设备事件接收->清洗->进入MQ的通用流程;
  4. 平台命令下发。提供接口给应用平台,后者可以给设备下命令;
  5. 设备状态维护。在离线、活跃、不活跃等状态;
  6. [可选]规则引擎。提供通用抽象功能,允许应用平台创建规则触发某些预定义动作。比如温度>37°自动打开喷淋开关并给用户推送高温预警。该功能亦可通过大数据平台实时流处理来实现。

本设计稿参考了thingsboard、水务平台、阿里云物联网平台,并根据我个人以往设计经验总结。

设备模型抽象

物联设备包括以下通用属性

  • ID,设备唯一标识;
  • 名称(name);
  • 类型(type)。类型可以分级方便用户选择,比如智慧园区(region)->人员管理(category)->考勤设备(type);
  • 品牌(brand)。设备制造商的标示;
  • 型号(model)。设备制造商给设备设计的型号;
  • 序列号(serialNumber,简称sn)。设备制造商给设备分配的编号;
  • 通信协议(protocol)。每个协议标识对应一个具体代码的通信模块。即使是同一件设备,不同固件的通信方式可能有所改变,比如v1和v2的接口可能不兼容;
  • 协议配置(protocolParam)。某些协议可能会依赖其他的字段,比如第三方平台的账号密码等,如果没有的话无法正常建立通信。这里可以选择租户已经添加的配置;
  • 其他扩展字段(extend)。比如设备通信地址(ip地址、蓝牙地址等),安装坐标;或者某类设备独有的属性,摄像头是球机和枪机、门禁是进还是出等等;允许自行添加kv字段,在数据库里可以用json/jsonb或者纵表;
  • 支持事件(event)。同一类(type)设备往往支持类似的事件,如环境设备都支持上传环境数据,但是具体到设备上要看安装了多少传感器;
  • 支持命令(command)。设备可执行的命令,同类(type)设备往往支持类似的命令,比如人脸识别设备一般支持远程开/关门;
  • 租户(tenant)。理论上设备只能同时属于同一个租户,但是可以考虑设备属于多个租户的需求。比如一个设备归属方,其他的属于设备监管方,也有权查看设备数据。当然这个归属问题也可以在应用层来做,平台这边简单设计设备就只有一个租户;

几个要点:

  • 设备ID应该由物联网平台生成,支持通过界面添加或者其他服务调用平台接口添加;
  • 设备的brand-type-sn,应该构成唯一性,即同一品牌同一类型的设备序列号唯一;
  • protocol决定了设备通信的方式,添加设备时可以通过设备类型+设备品牌筛选protocol;
  • 可用的protocol列表是与代码中的通信模块一一映射的,因此该列表由开发人员维护;
  • 标准HTTP接口和标准MQTT接口是我方提供的两种通用的protocol,选择这两种接入时,服务端会自动生成访问密钥;
  • 设备通过边缘网关接入和直连上云的唯一区别是协议参数需要选择对应的边缘网关,边缘网关和设备之间仍然使用协议模块通信,边缘网关和平台之间使用标准mqtt接口通信;
  • 当设备/边缘网关采用标准HTTP接口通信时,仅当设备/边缘端有固定外网地址时,才能接受云端下发的命令。有时需要考虑内网穿透问题;
  • 理论上,设备的brand-type-model-protocol决定了设备支持的事件和命令,以及事件/命令具体的参数;

综上,需要设计以下表单/API进行设备数据录入(以下字段并非数据库中的字段,仅为示例):

设备类型管理

字段 类型 备注
code int 类型码,枚举值,唯一标识,如200
name string 中文名称,如:考勤设备

为了避免各应用重复添加设备类型,该接口可以不开放,仅能通过界面手动添加。

如果需要级联的设备类型,则需要加入树结构管理,即parentId.

设备品牌管理

字段 类型 备注
id int 唯一标识,可以用自增
name string 如:海康
deviceTypes [int] 该品牌涉及的设备类型,为了方便搜索

品牌型号管理

品牌型号的录入,只是为了细化设备对应的协议和支持事件/命令,其实也可以没有。

字段 类型 备注
id int 唯一标识,自增
name string 型号名称
brandId int 品牌id
deviceType int 设备类型

连接协议管理

字段 类型 备注
code string 唯一标识,如std-mqtt
name string 中文名称,如标准MQTT接口
deviceType int 驱动对应的设备类型,默认0表示支持所有设备类型
connMode int 0-我方服务端(默认),1-我方客户端
netMode int 0-可以外网访问(默认),1-仅支持内网通信
params json schema 该驱动参数的json schema描述(后端)

该表由开发人员维护。在未正式接入实际项目之前,前期先实现std-mqttstd-http两类协议。

json schema是一种json数据结构自描述规范,前端需要据此动态渲染表格给用户展示,可以参考alibaba的x-render项目(react),或者vue的版本

协议关联管理

字段 类型 备注
brandId int 品牌id
deviceType int 设备类型
protocol string 支持协议

该表用于用户添加设备时,选取可用协议。

对于标准HTTP/MQTT接口,brandId设为0,表示该协议支持所有品牌。

之所以独立成表,是因为存在贴牌设备的问题,比如小米电视贴牌海信,实际通信协议是海信,即多对一可能。

也可以增加一个deviceModels字段,允许精确到设备型号进行筛选,不过维护列表的工作量较大。

协议参数管理

字段 类型 备注
protocol string 协议代码
name string 该配置的名称
tenantId string 租户id
gatewayId int 0-云端直连,其他:边缘网关的id
data json 对应protocol中的params

gatewayId决定设备是直接上云,还是通过边缘网关上云。

当设备仅支持局域网接入,而平台和设备无法在一个局域网时,只能通过边缘网关上云。如果设备可以使用第三方的边缘网关上云,则我方只需要对接该网关,此时视为直接上云,录入设备时协议选择对应硬件网关的协议即可;当平台和设备部署在同一个局域网时,也视为直接上云;仅当采用自研软边缘网关接入时,这里需要选择边缘网关的id。

data为一个动态表格,用户选择协议后填入依赖的参数。不同协议对应的字段需求不同,所以data内容不固定。对于前端,可以

由于协议依赖往往涉及到第三方的密码之类,所以必须进行数据隔离,加入tenantId字段。但是有时该协议参数为所有租户公用,此时可以设置tenantId为空,比如所有租户的视频都要接入我方租用的海康的ISC平台。当然

另外就是一些允许客户自定义的参数,如采集周期,限流设置等,也可以在协议参数里设置。

该表由运营人员或者售后人员维护,一般用户可能无法维护,涉及技术参数较多。

设备事件管理

字段 类型 备注
code string 事件唯一标示,如:EVENT_UPLOAD_ENV
name string 事件中文名,如:环境数据上传
data json schema 事件的json描述,通过该字段对接入数据进行validate和normalize
priority int 事件优先级,不同优先级的消息将被push到不同的kafka topic

data示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
{
"type": "object",
"title": "title",
"properties": {
"personId": {
"type": "string",
"title": "人员标识",
"description": "一般是人员的身份证号"
},
"photo": {
"type": "string",
"title": "图像",
"description": "base64或者url"
},
"timestamp": {
"type": "integer",
"title": "毫秒级时间戳"
},
"temperature": {
"type": "number",
"title": "体温",
"description": "保留一位小数",
"minimum": 30,
"maximum": 45,
"unit": "摄氏度"
},
"gender": {
"type": "integer",
"title": "性别",
"enum": [
0,
1
],
"enumDesc": "女\n男"
}
},
"required": [
"personId"
]
}

遵从json schema标准语法,也可以根据情况进行扩展。比如上文扩展了一个“unit”字段,表示标量值的单位。扩展字段需要单独出一套语法规范给对接人员参考。

即使是同一事件,不同设备的参数也可能不同。比如考勤上报时,有的设备有体温数据,有的没有。data一般为同一事件可能数据的并集

设备命令管理

字段 类型 备注
code string 命令唯一标示,如:CMD_TOGGLE_SWITCH
name string 命令中文名,如:切换开关状态
data json schema 命令的json描述,表示下达命令时需要额外填入的字段,比如status: 1或者0表示切换开还是关

显然命令和事件可以合并到一个表里,不过命令不支持优先级(除非设备本身支持)。

即使是同一命令,不同设备需要的参数也可能不同。比如开关可能支持定时参数,此时一般将命令拆分成多个。如这里可以拆分成即时开关和定时开关两个命令。

设备命令原则上不支持重试/上线重发机制,因为可能存在一定的危险性。即所有命令默认都是即时的,如果此时设备不在线,不会尝试在设备重新上线后再发送。

事件/命令关联管理

即设备支持哪些命令和事件的关联。

字段 类型 备注
deviceType int 设备类型,为0表示所有设备都支持
brandId int 设备品牌
modelIds [int] 设备型号列表
protocolIds [int] 协议列表
events [string] 支持事件列表
cmds [string] 支持命令列表

这里的设计比较粗犷,为了方便配置,这里在选择设备类型和品牌后,其他的都可以多选。如果要细化配置,可以针对每个type-brand-model指明事件参数,但是这样工作量较大,没有太大必要。

该关联对于规则引擎与用户交互,是必须的。对于应用层,也可以通过该关联直接判断设备是否支持某个命令。

边缘网关管理

字段 类型 备注
name str 中文名称
tenantId str 边缘网关对应的租户

其实边缘网关就是一个特殊的设备,与平台采用标准MQTT通信,所以服务端也会自动生成访问的token。

资产管理

资产是设备的聚合,严格来说这并不是接入平台需要考虑的功能。产品可以根据需求自行发挥。

但是需要注意的是,某些设备之间是有关联的。比如视频设备和视频AI设备,可以在录入视频AI设备时,在扩展字段里选择关联设备。

设备接入

设备接入采用模块化设计,每个**模块(Module)对应一个上文中协议(Protocol)**。

可以将每个模块写在单独的jar包里,通过bean的生命周期启动(@PostConstruct)/停止(@PreDestroy)服务,最后部署的服务只需要引入实际设备对应模块就可以完成最小化部署。但是saas情况下不宜采用该方案,jar包会非常多;也可以全部写在一个服务里,然后通过ConditionOnBean之类的方式通过配置动态开关服务,唯一缺陷是最终的package体积可能会比较大;或者两种方法进行折中,根据某种方式(如设备品牌)聚合一下。

一个Module的设计如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public interface IModule {
void onInit(); //初始化时启动模块协议
void onDestroy(); //模块关闭时清理资源
void call(Command command); //接受外部命令
}

@Data
@Accessors(chain = true)
public class Command<T> { //通用的命令,应用平台通过HTTP/RPC/Kafka发送过来
private String code; //对应上文中的设备命令,如CMD_TOGGLE_SWITCH
private String reqId; //请求的唯一ID,用于链路追踪
private String reqSrc; //请求来源服务的标示,如智慧工地/智慧园区的标示等
private Device target; //目标设备
private T data; //请求的具体参数
private boolean sync; //同步命令还是异步命令,默认异步
@JsonIgnore
private CompleteFuture<String> result; //请求结果,非传入值。请求为同步命令时,用于将异步转化为同步
}
@Data
public class Device {
private Long id;
private Integer type; //设备类型
private String sn; //设备序列号
private Long brandId; //设备品牌ID
private Long modelId; //设备型号ID
private String protocol; //设备协议
//...
}

Module一般是基于Netty编码,Bean的名字就是protocol的名字。

有一个专门的module负责处理与应用平台通信,接收到平台命令时,通过target的protocol找到对应的通信模块,调用call将命令转发过去即可。Netty一般在自己的线程里处理命令,需要同步返回结果的,将结果放入result字段;异步返回结果的,也可以通过reqId+reqSrc实现。

事件上报同样由专门的模块实现,其他模块调用该模块的call来上报事件。为了防止数据丢失,必须保证数据放入kafka(ack all)后,再对设备端发ack. 当然设备端必须有重发机制,否则无论如何设备数据都可能丢失。

如果简化设计,可以使用kafka全异步通信,将所有Event和Command都是通过kafka来传递。以下会按全异步设计来写具体方案,同步设计只需要用上面的result进行转换,也可以在平台外面wrap一层同步的HTTP/RPC接口。

接入层架构图如下:

这里标准MQTT还是选择用emqx作为broker,thingsboard内置的mqtt服务与thingsboard平台深度绑定,且不支持共享订阅,不建议使用。边缘端设备接入可以用emqx-edge(即设备本身就是用mqtt协议,且接入层部署在边缘端时)。

按着saas的设计,接入层全部在云端部署,此时接入集群共同订阅kafka的Command即可;还有一种方案是接入层整体部署在用户的本地(上面虚框部分),此时接入层整体视为一个边缘网关,云端只需要部署标准MQTT集群,该集群会将命令通过MQTT下发给接入层集群。接入层集群使用MQTT的共享订阅功能完成负载均衡。

如果用户有多个园区/工地/社区要管理,那么在园区/工地/社区本地部署接入层,在用户机房部署其他部分,这种设计可以派上用场。如果仅仅是单独的园区/工地/社区,那么全部部署在本地,此时设备全部视为直接上云。

接入层可以任意水平扩展,但是如果设备与服务端使用的是长连接,此时如果想要给设备发送命令,就必须知道设备实际连接到哪个结点了。下文会在设备状态维护里谈到该问题的解决方案。

接入模块作为客户端

理想情况下,接入模块应该是作为服务端,设备主动向平台发送数据。但是实际上设备很多时候连接了厂商的中控平台或者云平台。此时连接模块只能作为客户端,主动向中间平台请求数据。

该场景下仍然可以用netty来写模块,但是存在一个额外的问题。即多服务结点时,如何进行通信任务的分发

这里需要一个额外的服务来完成该工作,假设名为scanner。当租户添加的协议参数同时满足:

  1. 这是一个客户端模式的协议;
  2. 该协议为云端接入(非边缘模式);

则将该协议视为一个待分发的客户端。scanner定期扫描所有待分发的客户端,将其push到kafka Command topic里,视为一种特殊的Command。获取到Command的接入层结点call协议对应的接入模块,模块则根据协议参数增加定时任务或者建立长连接等。

模块接受Command之后,要标记该客户端为已分发(比如采用redis kv标记);在该服务节点关闭前,需要清除该标记,以便scanner重新分发该客户端。

边缘网关采用的是硬分配原则,所以边缘端从本地数据库扫描到客户端模式的协议配置后,直接启动服务即可。参考边云数据同步部分设计。

数据交互

设备事件上报

上文已经写了上报的大致流程,上报到kafka的数据结构大致如下(json格式):

1
2
3
4
5
6
7
8
9
{
"code": "EVENT_UPLOAD_DATA", //事件编码
"ts": 1234567890, //毫秒级Unix时间戳
"deviceId": 12345, //设备id
"tenantId": "abc", //设备此时的租户id
"data":{ //具体的事件数据

}
}

根据事件的优先级投递到不同的topic. 在投递之前可以根据event的schema进行校验和规格化(也可以不处理,后续再进行流式处理)。

kafka后接不同消费者组,再对数据进行进一步处理,本平台会实现数据落库以及推送到规则引擎。大数据组或者应用组也可以订阅该数据做进一步处理,如推送给大屏/客户端等。

设备连接状态维护

设备连接状态的变化其实就是一类特殊的事件,比如可以命名为EVENT_DEVICE_TOGGLE_ONLINE.

设备在线一般是通过应用层心跳来维持的,如果采用长连接,可以用netty的IdleStateHandler来设置最小活跃周期(通过读取协议参数);但是如果是HTTP短链接的话,每次请求可能router到不同接入层结点上,因此不能在内存中维持。此外如果有查询平台/租户所有在线设备等统计需求,也不能只将连接状态维护在内存里。

这里设计一个简单的解决方案:使用lua脚本,将设备最近一次心跳时间记录在redis zset里(可以根据最小活跃周期设置多个zset)。然后定时(周期比最小活跃周期小即可)扫描这些zset,确定心跳过期后认为设备离线。

设置设备在线示例代码如下,假设记录的key为iot:device:heartbeat:120,其中120表示心跳过期时间为120秒:

1
2
3
4
5
6
7
8
9
-- argv[1]: 设备id, argv[2]: 心跳时间戳
-- ret[1]: 是否由离线变为在线
local old_ts = tonumber(redis.call('zrank', KEYS[1], argv[1]))
local update_ts = tonumber(argv[2])
-- 如果原来的值不存在,直接更新;或者时间戳更新
if old_ts == nil or old_ts < update_ts then
redis.call('zadd', KEYS[1], update_ts)
end
return old_ts == nil

Redis6.2之后可以直接用GT选项完成上述功能,不再需要lua.

离线扫描直接用ZRANGE配合ZREMRANGEBYSCORE即可完成,也是使用lua脚本保证原子性。

当然也可以用其他方案来完成该任务,比如etcdwatch等,可以根据平台选型来实现。上述方案是个单点方案,有更好的方案欢迎提出。

对于长连接,当连接建立时,需要在redis中记录设备和服务结点的双向映射。这样当Command的消费者结点接收到命令但是发现设备连接在另一个结点时,可以将命令转发到另一个结点。

当服务结点关闭时,长连接模块的清理方法需要将所有关联结点设置为离线,然后逐个断开连接并关闭服务。

注意:一般不考虑服务端宕机或者网络断开问题,否则要处理的边界条件会大幅增加。

应用命令下发

如上文所述,平台可以提供HTTP/RPC/Kafka等多种通信方式供其他平台下发命令,但是考虑到某些命令的执行周期会很长(如向人脸识别设备下发1w个人员信息),全部设计为异步接口更加泛用。

假设这里使用Kafka通信,所有接入层结点对Command成为一个消费者组。接入层结点有一个专门的模块,假设为module-command,订阅该topic,接收到Command之后查找Module,调用call发送命令. 如果是长连接模块(短链接模块直接执行命令即可),该模块要检查连接是否在本结点,如果是的话直接执行就行;不是的话要考虑转发或者返回”目标不在线”的错误信息。

注意上面未考虑接入层服务拆分问题。如果拆分的话,还需要一个protocol到服务节点的映射。比如std-mqttstd-http是两个独立的服务,组成一个Command Topic的消费组,前者拿到命令发现设备实际使用后者通信,那么需要一个机制找到后者服务节点,然后转发命令。

理论上所有的命令都一个返回值,由于是全异步通信,Command类中就不再需要result字段。netty线程中执行完命令后,直接调用module-commandcall,后者将数据push到kafka中专门应答的topic中即可。

设计数据结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Data
@Accessors(chain = true)
public class Command<T> { //通用的命令,应用平台通过HTTP/RPC/Kafka发送过来
private String code; //对应上文中的设备命令,如CMD_TOGGLE_SWITCH
private String reqId; //请求的唯一ID,用于链路追踪
private String reqSrc; //请求来源服务的标示,如智慧工地/智慧园区的标示等
private Device target; //目标设备
private T data; //请求的具体参数
}
@Data
public class Response<T>{
private String cmd; //对应请求中的code
private int status; //状态码
private String msg; //应答描述,一般是错误信息
private String reqId;
private String reqSrc;
private T data; //具体数据
}

应用层订阅该应答的topic,即可获取命令执行的最终结果。可以按reqSrc分为不同业务的topic,避免数据混乱。

运行时配置变更

显然,在接入层通信过程中,应用平台依然可以修改设备信息、协议参数等关键配置,这些配置变更必须立刻通知到接入层。

一个方案是使用maxwell/canon等组件订阅mysql binlog,发现对应表格数据变化后,向kafka Command Topic中push一条特殊的命令。订阅端清除redis中相关的缓存,并通知对应的模块断开原来的连接等。

如果不想引入额外的组件,也可以在变更save到db之后,手动执行该操作。

边云数据同步

当接入层在边缘侧时,需要考虑的问题要更加复杂一些。

边缘网关启动后,需要定时拉取该网关关联的设备列表和对应的协议参数,并将其save到本地数据库(以保证边云连接断开时,边缘端仍可正常运行)。可以根据数据最后更新的时间(update_time),决定本地对应条目是否需要更新;或者通过update_time进行筛选增量拉取;

当配置参数变更时,消费端发现该设备/协议项是通过边缘端接入的,则会直接向mqtt broker publish qos为0对应的信息,边缘端也可以根据该消息进行即时处理。

但是由于边缘端和云端的连接无法保证稳定性,边缘端简单设计的话,仅进行定时拉取即可

对于一般的Command,消费者模块发现protocol配置为边缘接入时,也可以直接publish到边缘网关对应订阅的topic,qos也应该为0。

微服务设计汇总

  • 接入层。接入层整体作为一个集群,可以全部写在一起,也可以根据需求进行拆分。比如std-mqtt和std-http都作为单独服务。拆分后注意接入层内部需要转发平台的命令;
  • 单实例的scanner服务(负责客户端模式下的任务的分发),和心跳检测服务,可以写在一起。可以考虑其他方案;
  • web服务。基本的API供其他服务调用(根据技术选型,也可能是rpc服务);如果平台需要做一个界面,相关接口也写在这里;
  • 物联数据落库服务。订阅kafka数据流并落入时序数据库;
  • 规则引擎服务。设计见下文;

组件使用汇总

kafka相关设计汇总

综上所述,kafka至少设计以下topic:

  • 事件上报的topic. 根据优先级可以分为多个;如果有些数据需要经过kafka stream处理后再落库,可以单独划一个优先级;
  • 命令下发的topic. 所有接入层服务结点组成一个消费者组;运行时配置变更可以作为特殊的Command,比如code为CONF_前缀;

redis相关设计汇总

  • 设备配置信息会缓存在redis中,避免与设备通信时频繁查库。当收到配置变更Command时清除缓存;
  • 设备连接状态会缓存在redis中,可以查询到在线设备id;
  • 设备与服务结点的长连接映射会存储在redis中,可以查找到设备连接在哪个服务节点上,也可以查找服务节点上具体连接了哪些设备;

部分功能可以使用别的组件来实现,redis只是说能做。

mqtt设计汇总

  • 注意使用emqx的共享订阅,保证服务节点之间的负载均衡;
  • 标准MQTT接入只有事件上报的topic和命令下发的topic;
  • 边缘网关使用mqtt通信,必须设置非内存的store,保证qos>0的数据不会遗失;
  • 边缘网关应用层心跳使用qos为0的通信,其他信息使用qos为1的信息;
  • 边缘网关只关心自己负责设备相关的配置的变更,所以推送的topic中应含有边缘网关的id,如iot/edge/1/device/update,表示给ID为1的边缘网关推送设备信息变更,iot/edge/2/protocol/create给ID为2的边缘网关推送新增的协议信息;当然如果采取定时拉取的方案,不必设计这块的东西

规则引擎

规则引擎的设计较为复杂,需要进一步调研,thingsboard的规则引擎其实是一个数据流处理器(类似flink)。

这里给出原来的设计方案以供参考,该方案较为简单,但是可以满足一般的设备联动和自定义告警需求。

通过设备模型抽象,上文中已经明确了设备支持的事件和命令。规则引擎本质上就是when event happens,if match some condition, then do actions.

我们已经有了event和action,以及他们的参数。那么前端就可以根据这些参数,通过一门DSL,就可以组合出condition。简单的DSL可以直接使用json.

比如condition这里使用mongo query表达式,例:

1
2
3
4
5
6
7
8
{
"key1":{"$lt": 8}, //key1小于8; $lt(<), $gt(>), $le(<=), $ge(>=), $ne(!=), $eq(=)
"key2":{"$ge": "37.3"}, //key2大于等于37.3
"$or":[{"key3":{"$regex": "\d{3}"}},{"key4": "3"}], //key3满足正则,或者key4=3,使用$like表示字符串包含
"key4":{"$in":[1, 2, 3]}, //key4=1或2或3
"key6.0": 10, //数组第一个元素是10
"key7.subKey1":{"$lt": 10}, //object的子元素小于10
}

前端根据用户选择拼凑出条件,就是规则引擎的可视化。

我们定义一个命令(deviceType=0),比如叫SYSTEM_NOTIFY,表示系统通知。参数是通知方式(短信、微信、电话等),通知频率等。用户设置condition,并选择该命令,这就完成了通用的告警配置。

用户也可以配置condition之后,选择一个设备,配置action,就可以完成设备联动。比如当环境监测仪上报的光照度< nLux之后,就打开某个路灯的照明开关。

复杂的规则引擎支持规则链,或者等待多个条件等等。可以考虑借鉴工作流来实现。

其他相关平台

  • 鉴权服务。该平台不开放给普通用户使用,因此本设计方案里不涉及用户和权限部分。可以与其他内部平台共享同一套鉴权系统;另外需要注意,上文设计时仅考虑了单业务系统,如果是多业务系统租户id可能重复的时候,还要加上一个业务系统ID字段;
  • 数据分析服务。接入平台只负责将物联数据写入到TSDB,数据分析需要独立的服务;
  • 大数据流处理。原始的物联数据可能无法满足业务层的需要,一个典型的例子是人脸识别只能上报人员身份标示(如身份证号),该数据需要和业务系统中人员数据关联起来,才能被业务使用。此时就需要使用kafka streams等技术进行二次加工数据;
  • 全平台日志审计。关键业务数据的增删改需要记录操作日志,以免出现问题时无法溯源;
  • 推送系统(实时/离线,多渠道)。某些物联数据可能需要实时推送给客户端;规则引擎的告警也需要实时推送。显然应该有一个公用的推送系统;