IoT 设备指令下发接口设计成同步的还是异步的?

2021-02-09 10:49:27 +08:00
 Vieufoux
现在需要写一个后台服务,用户通过 HTTP 请求发送控制指令到后台,后台通过 MQTT 下发指令给设备端,设备再通过 MQTT 返回控制结果给云端。

我看了涂鸦下发设备指令的接口( https://developer.tuya.com/cn/docs/iot/open-api/api-reference/api-list/device-control?id=K95zu01ksols7#title-26-%E4%B8%8B%E5%8F%91%E8%AE%BE%E5%A4%87%E6%8C%87%E4%BB%A4 ),它是一个同步接口,也就是发送了 HTTP 请求之后就一直等待在那儿

我的疑问是 HTTP 请求发送到服务器之后请求一直等待在那儿吗?这样并发量大了之后不是很耗资源吗?想知道涂鸦内部是怎么实现的?
2900 次点击
所在节点    程序员
20 条回复
xylophone21
2021-02-09 11:04:16 +08:00
参考 AWS 、阿里云的设备影子

另外涂鸦这个接口从哪里可以看出来是同步的?
Vieufoux
2021-02-09 11:10:07 +08:00
@xylophone21 谢谢,我的意思是这个接口发送请求之后就等待在哪里一直等到设备端的返回结果。我认为异步的实现是发送请求之后,服务器立刻返回一个 msgid,然后调用者再根据 msgid 去查询控制结果。
Slartibartfast
2021-02-09 11:10:10 +08:00
看文档确实是同步的,这样上层代码好写。

实际自己做底层逻辑的话,应该提供同步异步双接口
sxyclint
2021-02-09 11:27:58 +08:00
一楼说的没错,影子设备其实是比较好的实现,这样你接口设计成同步都没问题,反正就是修改影子的状态。
soulzz
2021-02-09 11:30:43 +08:00
底层 netty 写的话绝对是异步,然后用回调时用流水号或指令 id 做回调区分。
前台下发指令,后端会根据设备通讯协议下发字节流到设备,然后设置超时等待。
收到设备回复的接收到指令的消息回复,新建一个 Future 实例并缓存,然后等待设备的执行结果回调,收到执行结果的时候将 Future 实例移除,并对取出的 Future 进行设值。
在超时等待时间段内如果有回调就返回执行结果,没有就返回超时或者失败。
这样对前端或者第三方指令接入友好。
当然,如果是用 websocket 写的接口就不用这么麻烦,约定好回复结构,实时返回指令下发与接收过程的每一步
swulling
2021-02-09 11:40:07 +08:00
一般这种非立刻生效但是耗时预估也不会太久(<10s )的接口,有三种实现模式

同步接口:优点是上层业务逻辑简单,缺点调用耗时较长,接口并发性能较差。
而且偶尔超时后就比较麻烦,有可能状态不一致。比如下发的动作执行了,但是返回时超时。


异步接口:优点是实现简单,还可以利用消息队列等方式来提升性能,理论上接口不会成为性能瓶颈。缺点是上层的业务开发比较复杂,需要自己实现轮询状态或者用长链接监听状态。


声明式接口:上层只需要下发期望,比如下发灯亮这个期望,设备定时去往这个期望变化。这种就类似于 Kubernetes 的接口,优点是能够保证最终一致性,缺点是还需要配合查询接口来轮询或者长链接 Poll 的方式看下当前状态是否达到期望。


目前从开发习惯上,还是同步或者异步接口比较多,但是复杂控制系统声明式接口有它非常大的优势。
swulling
2021-02-09 11:41:08 +08:00
上面说的影子设备、Future 实例等,都是声明式接口的一种变种。
Vieufoux
2021-02-09 11:51:29 +08:00
@xylophone21 @xylophone21 谢谢,设备影子确实可以做到立刻返回
Vieufoux
2021-02-09 11:57:03 +08:00
@swulling 谢谢,我之前就是实现了同步接口,确实会有一些临界 case 导致设备端执行成功但是 HTTP 请求显示超时了。后来又尝试了异步接口,客户端轮询,这种方式上层实现麻烦而且不能保证实时性。
laminux29
2021-02-09 12:37:49 +08:00
科学的方式,是把整个业务进行过程式细化,然后接口设计为异步,并且提供过程状态查询接口。

比如:
用户通过 http 请求发送到指令到后台后,就不需等待,只需要调用过程状态查询接口,来查询目前后台处理状态。处理状态有且不限于:

1.查询不到请求。也就是用户 http 请求可能根本没发送到后台。

2.后台已经收到请求,后台正在向设备端下发指令。

3.后台在向设备端下发指令时出错,错误信息是:.....

4.设备端已经收到指令,等待设备端处理。

5.设备端处理超时。

6.设备端处理时发生错误,错误信息是:......

7.设备端已经处理完毕,目前设备端的状态是:......

这种设计,缺点是工作量比较大,优点是科学、严谨、用户体验好。
xylophone21
2021-02-09 14:14:45 +08:00
这里楼主给的链接里关于返回参数的说明

返回参数
参数名 类型 说明
code Integer 错误码。
success Boolean 判断请求是否成功。
true:成功
false:失败
msg String 请求失败返回的信息,成功则返回空值。
result Boolean 是否成功。

返回的信息里只有是否成功,并没有说这个成功是执行还是请求还是修改影子,当然这是文档的问题。

但一般情况下这个场景不需要同步接口,因为下行通道是必须的,否则如果其它控制渠道(比如物理面板,物理遥控器)的控制就没办法反馈了。
tienhua
2021-02-09 14:27:17 +08:00
我的经验是采用 grpc 的 bidi stream 方案
BingoXuan
2021-02-09 16:08:35 +08:00
异步,mqtt 本身就是通过队列控制 iot 的状态,HTTP 只是查询。HTTP 更新 iot 状态,设备获取最新订阅消息然后执行,无论成功与否,更新最新状态,在最新状态更新之前,HTTP 都能及时获取最后设备更新的状态。
Vieufoux
2021-02-09 19:02:06 +08:00
@tienhua 多谢,我研究一下
Vieufoux
2021-02-09 19:06:32 +08:00
@BingoXuan 谢谢,我理解这种实现就是设备影子,但是怎么能知道设备状态是否真的更新成功了?
janxin
2021-02-09 19:32:13 +08:00
底层肯定是异步啊...
noroot
2021-02-09 19:40:17 +08:00
你说的应该是后台通过 MQTT 发指令到网关,网关处理这些指令并转发相关消息到终端网络吧。

这个接口大概只是客户端同步等待,服务器自身异步处理(简单的方法可以是将你的请求注册到相应的 MQTT 主题下)。
micean
2021-02-09 20:31:34 +08:00
页面设备状态的刷新避免不了长连接或者轮询请求
huai
2021-02-10 00:24:15 +08:00
@swulling 不太理解二三具体区别,能否解释一下。我理解都是下发指令 ,轮训来刺探结果
BingoXuan
2021-02-10 09:06:43 +08:00
@Vieufoux
mqtt 协议本身就有 qos 控制,可以确保工作正常状态下消息更新。更新时候把时间记录下来就好了,对比距离最后更新时间就行。你甚至可以用 ws 转 mqtr 做成类即时的效果,只要 mqtt 的 broker 支持这种特性

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/752548

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX