阿里云IoT应用案例
最近半年一直在尝试物联网相关技术在传统行业的应用,推动企业数字化转型。结合阿里云和阿里云IoT平台,初步实现了几个主要业务功能,整理分享如下:
1. 背景
核心诉求是终端设备接入物联网:一方面是为了数据的远程交互,另一方面是数据的收集和应用
目前终端种类繁多,未统一平台和标准,操作系统覆盖Windows、Android、Linux. 而IoT模组又依据主流通讯技术分成2G、3G、4G和NB-IoT,不同通讯技术的模组使用的协议不尽相同,这对于终端仪器开发的同学来说是个痛点,各个操作系统和开发语言均需对接IoT模组,一旦模组被更换,又需要重新匹配新的协议。
数据接收端也不好过,不同的运营商有自己的IoT平台,管理物联网卡和数据收发。更有运营商强制要求指定型号的模组只能用自家平台进行数据管理(如移远 BC95-5 只能使用电信Easy IoT平台)。应用系统开发需要维护多个运营商IoT平台,接口亦需开发多套。
所以我们想设计一套解决方案,在满足核心诉求的前提下,减少终端和应用系统接入IoT的难度。
2. 架构
阿里提供了一套Link Kit SDK,如下图所示,用于终端快速接入阿里云IoT平台
Link Kit SDK首先是支持多语言及跨平台的,支持MQTT、CoAP、HTTP/S协议,数据直接对接阿里云IoT平台,“没有中间运营商赚差价”,对于不支持TCP/UDP的终端,阿里也提供了网关做统一收发,这就让架构精简了不少。但依然存在一些没填平的坑:
终端开发仍需要学习 Link Kit SDK,而应用开发需要学习阿里云AIoT平台
SDK支持的协议只能发送到阿里云IoT平台,数据转储也只能发送到MQ、RDS等阿里云的SaaS或PaaS。同时目前首国家与运营商的政策,某些模组只能接入运营自己的平台(前文提到的BC95-5),云服务商并不能通吃
SDK需三元组注册,注册分两种,但不论哪一种都比较麻烦,批量的设备更希望动态注册
- 一机一密:在设备上烧写设备的ProductKey、DeviceName、DeviceSecret,然后适配相应的HAL并调用SDK提供的连接云端的函数即可,这种方式要求对设备的产线工具进行一定的修改,需要对每个设备烧写不同的DeviceName和DeviceSecret;
- 一型一密:设备上烧写设备的ProductKey、ProductSecret,每个设备需要具备自己的唯一标识并将该标识预先上传到阿里云IoT物联网平台,然后调用SDK提供的函数连接云端。这种方式每个设备上烧写的信息是固定的ProductKey、ProductSecret
最终设计了一套基于阿里云IoT且更为通用的解决方案架构
我们根据不同的操作系统、不同的开发语言做中间件,中间件再调用Link Kit SDK,同时中间件实现动态注册和封装一些面向上位机的标准化业务接口,如数据通讯、文件下载、远程控制等。对于不能接入阿里云IoT的模组,中间件还需要直接对接到运营商平台。
对于终端设备上位机来说,中间件就是SDK,他们只需按需调用标准化的业务接口,无需关注IoT模组、协议即可快速接入IoT平台。而对于后台系统,我们也从阿里云IoT平台和运营商平台收集数据,进行标准化后再输出给应用系统。从而实现双向快速接入IoT.
3. 技术
3.1 阿里云MQ的设计和使用
中间件包含阿里云IoT和运营商直连两部分,这里我们只讨论阿里云IoT平台这一块,数据流转方式如下图所示,其中SDK服务端订阅模式支持的语言有限,使用场景有限,根据终端数量和TPS,最终选择了规则引擎转发到RocketMQ
阿里云IoT平台目前在国内只有华东2有节点,但平台的规则引擎可以转发到任意一个区域节点,比如我们的RocketMQ实例在华南区,又因为RocketMQ的Topic、Tag机制如下图所示:
所以整体方案架构为:
- 一个应用实例可以开一个RocketMQ消费者/生产者,用一个GID,其集群也是同一个GID
- 消费者、生产者可注册或订阅多个Topic
- 用DeviceID作为Tag用于区分是哪个终端发的消息
- 在物联网平台需针对物联网平台的产品Topic设置转发规则到RocketMQ
3.2 rocketmq-client-go踩坑
由于我们技术栈是Go系,阿里云IoT平台的MQ是Rocket,原生只支持Java和C++,HTTP协议虽然通用,但效率过于低下,Go的SDK是社区版,阿里云支持有限。经过一番摸索,也算是填平了几个大坑。
首先Go的SDK分原生和非原生,非原生是cgo加RocketMQ C++SDK,在 https://github.com/apache/rocketmq-client-go 的Master分支下,使用时间较久,稳定性较高,但编译麻烦。
原生在 https://github.com/apache/rocketmq-client-go native分支下,是纯go,无需SDK,看上去更像是个完美解决方案,但native尚在开发阶段,支持的功能少,作者也不建议在生产环境使用。
最后我们还是选择使用非原生版本,需要提醒的是当前(2019.11),github的用户文档会产生一些干扰和误解,官方指导是下载RocketMQ的C++版本和GCC,并提供了一个编译后的bin文件,但这个bin文件版本已经过期了,不能对应最新的rocketmq-client-go (1.2.2 与 1.2.4),虽然程序能跑起来,但是会产生很多影响功能的问题。
正确的做法是参考阿里云的文档 https://help.aliyun.com/document_detail/140296.html?spm=a2c4g.11186623.6.619.7fb56e2bIv135O ,官方提供了不同系统环境下的GCC、CGO及SDK的安装方式。由于我们用的K8S,所以使用了Docker镜像并通过Dockerfile来完成RocketMQ的使用。
1 | // 安装 gcc |
4. 功能
4.1 注册
上文也说到阿里云IoT的设备注册可以批量烧录,也可以分配三元组。但对于批量设备注册,还需一套完整的动态解决方案。语雀社区也给了一些思路 https://www.yuque.com/cloud-dev/iot-tech/qtg9m9
方案是通过调用阿里云IoT平台的API来实现动态分配三元组的。根据中间件的设计需求,我们调整了时序图如下所示:
首先是设备启用和新增,应用后台可以新增设备,新增一个设备ID并传递给阿里云IoT平台用于申请三元组,注意设备ID必须唯一,应用平台需控制,不允许出现相同的设备ID传递到IoT平台,否则数据会紊乱。新增、启用设备主要是应用后台申请三元组并存储在应用系统的DB中。
当应用系统申请到三元组后,中间件才能使用,否则会拿不到三元组从而导致无法注册到IoT平台。中间件一旦启动,会调用应用系统的注册接口,注意这部分请求不是通过Link Kit SDK去实现的,所以需要中间件实现一个HTTP(S)的请求。通过这个请求,中间件把仪器端的设备ID传递给应用系统(设备ID与应用系统添加的一致),应用系统将DB中的三元组返回给中间件,中间件再使用三元组完成阿里云IoT平台的注册。
最后,当应用系统想停止某个仪器设备,就可以在应用后台调用停用设备的接口,则应用后台会通知阿里云IoT删除对应的三元组,并删除应用系统DB的三元组数据。中间件就会因为三元组的丢失,不能再向后台发送数据。
4.2 软件更新(非ota)
软件更新这块,阿里云提供了官方的OTA方案,我们并没有完全去使用,一方面考虑方案的灵活性,另一方面OTA目前还不算特别成熟。我们是基于阿里云OSS和IoT平台来实现软件的远程更新。
首先是应用系统针对软件有一个版本管理,包括软件包的上传(上传至OSS)。
然后是推送功能,推送功能可以让用户选择在线的仪器,则应用后台会向选中仪器的中间件发送一条消息,标识有软件更新,并提供下载连接(OSS/CDN下载连接)。
中间件收到消息后,静默下载升级包到本地临时文件,并校验安装包完整性。
当安装包下载完整无误,再通知上位机,并传递本地路径。
最后用户执行或自动策略执行升级文件,完成升级。
同理,任何文件格式的传输都可以采用这种方式,比如配置文件等。
4.3 远程控制
远程控制这块,有现成的轮子 https://github.com/zhaojh329/rtty ,主要想通过远程的ssh,实现故障快速定位,降低运维成本。这里就不再多说了。
以上就是我们近期在IoT实践中的部分总结。
阿里云IoT应用案例