RTIO提供设备上云的端到端解决方案,主要包括设备接入、设备代理、后端服务代理等功能:
- 用户端使用HTTP GET、POST方法通过RTIO控制和实时观察设备(通常用户端下发指令、观察设备状态)
- 设备通过GET、POST接口访问RTIO代理的资源(通常设备端获取服务端资源、上报事件)
- 用户可访问NAT网络后的设备
native │ could
│ ┌───────┐ ┌──────────┐
┌──────────┐ tcp/tls │ │ ├─────► device │
│ device ├───────────┼─────► │ │ verifier │
└──────────┘ │ │ │ └──────────┘
│ │ RTIO │
┌──────────┐ http/https│ │ │ ┌──────────┐
│ user ├───────────┼─────► │ │ device │
└──────────┘ │ │ ├─────► services │
phone/web/pc... │ └───────┘ └──────────┘
- REST-Like模型,其参考RESTFul模型
- 用户端通过HTTP访问设备,无需集成SDK
- 用户端通过HTTP即可实时观察设备(非轮询)
- 解耦资源实现和资源调用者(以URI标识不同资源或能力,无topic耦合)
- 单节点支持百万连接(压测报告)
RTIO简化设备接入云端开发。设备也是服务资源的提供者,RTIO采取REST-Like模型,使设备端开发具有和WEB服务开发相似的体验和高效。主要有以下优势:
- 以URI标识不同资源或能力,比如设备提供V2/V3接口,方便设备资源或能力迭代
- 通过GET、POST和ObGet(Observe-GET)等方法交互,流程更简单(相比MQTT减少一半以上交互,可参考FQA部分)
- 解耦资源实现和资源调用者,可使用工具独立测试(类似使用curl/postman工具验证WEB服务接口)
REST-Like模型,这里指类似REST模型,但不以HTTP协议作为底层协议,模型提供了GET和POST方法和ObGET(Observe-GET,观察者模式)等方法。
RTIO交互示例可参考下文RTIO和MQTT服务交互比较。
环境需要安装golang(1.20版本以上)和make工具。
git clone https://github.com/guowenhe/rtio.git
cd rtio
make
成功编译后,out目录下会生成执行文件,主要文件如下,rtio为服务端,printer为设备端。
$ tree out/
out/
├── examples
│ ├── printer
│
├── rtio
运行示例,启动rtio服务(可通过-h参数查看帮助)。
$ ./out/rtio -disable.deviceverify -disable.deviceservice -log.level=info
INF cmd/rtio/rtio.go:77 > rtio starting ...
另一终端启动设备,模拟打印机。
$ ./out/examples/printer
INF internal/deviceaccess/access_client/devicesession/devicesession.go:675 > serving
另一终端模拟用户端,下达开始打印指令。
$ curl -X POST "http://127.0.0.1:17317/cfa09baa-4913-4ad7-a936-2e26f9671b04/post_handler" -d '{"uri":"/printer/action","id":12667,"data":"c3RhcnQ="}'
{"id":12667,"code":"CODE_OK","data":"cHJpbnQgc3RhcnRlZA=="} #
模拟用户端,持续获取打印进度。
$ curl -X GET "http://127.0.0.1:17317/cfa09baa-4913-4ad7-a936-2e26f9671b04/obget_handler?uri=%2Fprinter%2Fstatus&id=12334&data=MTIzNDU%3D"
{"result":{"id":12334,"fid":0,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgNDUl"}}
{"result":{"id":12334,"fid":1,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgNDgl"}}
{"result":{"id":12334,"fid":2,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgNTIl"}}
...
{"result":{"id":12334,"fid":18,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgOTEl"}} # data部分base64解码后:printing 91%
{"result":{"id":12334,"fid":19,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgOTQl"}}
{"result":{"id":12334,"fid":20,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgOTgl"}}
{"result":{"id":12334,"fid":21,"code":"CODE_CONTINUE","data":"cHJpbnRpbmcgMTAwJQ=="}} # data部分base64解码后:printing 100%
{"result":{"id":12334,"fid":22,"code":"CODE_TERMINATE","data":""}}
设备端代码,handler实现:
func handlerAction(req []byte) ([]byte, error) {
log.Info().Str("req", string(req)).Msg("")
return []byte("print started"), nil
}
func handerStatus(ctx context.Context, req []byte) (<-chan []byte, error) {
log.Info().Str("req", string(req)).Msg("")
respChan := make(chan []byte, 1)
go func(context.Context, <-chan []byte) {
for {
select {
...
case <-t.C:
respChan <- []byte("printing " + strconv.Itoa(progress) + "%")
}
}(ctx, respChan)
return respChan, nil
}
}
设备端代码,给URI注册handler:
session := ds.NewDeviceSession(conn, deviceID, deviceSecret)
...
// URI: /printer/action 0x44d87c69
session.RegisterPostHandler(0x44d87c69, handlerAction)
// URI: /printer/status 0x781495e7
session.RegisterObGetHandler(0x781495e7, handerStatus)
备注:
- 设备集成具体步骤参见设备SDK。
- GET/POST每次交互都带有URI标识,为压缩URI数据量,设备和RTIO服务交互过程采用4字节的哈希摘要(CRC32)通信,可使用rtio-urihash工具计算。
- 用户端Javascript可参考:user.html。
- rtio-device-sdk-go 支持Golang
目前完成设备端到服务端接入和Demo示例,rtio核心部分:
- HTTP远程调用设备端资源(Get、Post、ObGet)
- 设备端调用后端服务资源(Get、Post)
-
RTIO和MQTT服务有何不同?
他们都能与NAT后面的设备通信,MQTT为发布订阅模型,是多对多的模型,RTIO是点对点模型。
点对点通信场景中,MQTT通常需要定义一对topic(*_req和*_resp)来实现请求和响应,而这对topic相关的处理函数(Handler)耦合在两个系统中。RTIO通过URI标识不同资源或能力,解耦资源实现和资源调用者。
-
什么场景选择点对点通信?U2M(User To Machine)选择哪种模型更合适?
“点对点通信”为两台设备间通信,“多对多通信”即多台设备间通行。“多对多”通信有多种实现模型,这里主要对比MQTT的“发布订阅”模型。
U2M为用户端(比如手机)设备控制IoT设备,是典型的点对点通信。如果采用发布订阅作为通信模型,也可实现对IoT设备控制。但相比点对点通信会复杂许多,可参考下文“RTIO和MQTT服务交互比较”。
另外,一个用户控制多台IoT设备,点对点通信是否适用?如果IoT设备间不进行复杂的交互(比如10台打印机之间没交互),这里用户相当于批量的对打印机控制,仅增加批量的控制逻辑(比如循环对10台打印机处理)即可,该场景点对点模型仍比发布订阅模型交互简单。
-
RTIO名字含义?
RTIO - Real Time Input Output。“实时输入输出”,这里的“实时”指在期望的时间完成通信,否则返回超时错误。通过同步通信方式使IoT(特别是远程控制IoT设备)开发更简单。
-
RTIO和MQTT服务交互比较,请求到云端获取version列表
sequenceDiagram Title: get versions over mqtt participant device as mqtt-device participant broker as mqtt-broker participant app as app-server app ->> broker: SUBSCRIBE toppic: $diviceid_get_versins_req broker -->> app: SUBACK device ->> broker: SUBSCRIBE toppic: $diviceid_get_versins_resp broker -->> device: SUBACK device ->> broker: PUBLISH toppic: $diviceid_get_versins_req broker -->> device: PUBACK broker ->> app: PUBLISH toppic: $diviceid_get_versins_req app -->> broker: PUBACK app ->> app: query versions app ->> broker: PUBLISH toppic: $diviceid_get_versins_resp broker -->> app: PUBACK broker ->> device: PUBLISH toppic: $diviceid_get_versins_resp device -->> broker: PUBACK
sequenceDiagram Title: get versions over rtio participant device as rtio-device participant rtio as rtio-server participant app as app-server(resource-server) rtio ->> rtio: auto discover resource and registry device ->> rtio: GET /resource/versins rtio ->> app: GET app -->> rtio: versions rtio -->> device: versions
-
用户控制设备开关
sequenceDiagram Title: power on over mqtt participant device as mqtt-device participant broker as mqtt-broker participant app as app app ->> broker: SUBSCRIBE toppic: $diviceid_led_power_resp broker -->> app: SUBACK device ->> broker: SUBSCRIBE toppic: $diviceid_led_power_req broker -->> device: SUBACK app ->> app: user power on app ->> broker: PUBLISH toppic: $diviceid_led_power_req broker -->> app: PUBACK note over app,broker : means brocker successfully received not device broker ->> device: PUBLISH toppic: $diviceid_led_power_req device -->> broker: PUBACK device ->> device: led power action device ->> broker: PUBLISH toppic: $diviceid_led_power_resp broker -->> device: PUBACK broker ->> app: PUBLISH toppic: $diviceid_led_power_resp app -->> broker: PUBACK
sequenceDiagram Title: power on over rtio participant device as rtio-device participant rtio as rtio-server participant app as app app ->> app: user power on app ->> rtio: POST $device_name/led_power note right of app : could set timeout rtio ->> device: POST /led_power device ->> device: led power action device -->> rtio: led_power status rtio -->> app: led_power status
-
观察者模式(MQTT服务没有此模式)
sequenceDiagram
Title: rtio observation
participant device as rtio-device
participant rtio as rtio-server
participant app as app
app ->> app: observe progress
app ->> rtio: OBGET $device_name/progress
rtio ->> device: OBGET /progress
device -->> rtio: 20%
rtio -->> app: 20%
device -->> rtio: 30%
rtio -->> app: 30%
note over app,device : The observation can be terminated by device or app
device -->> rtio: 100%
rtio -->> app: 100%