我正在参与掘金创作者训练营第 4 期,点击了解活动详情,马上报名一起学习吧!
前言
最近做公司App的一些远程控制设备的一些方案研究,大概的流程如下:
这也算是一个比较主流的问题吧:
App将操作指令通过网络请求发送给后台,后台发给相应的设备,设备接受到指令后,进行对应的操作,操作完成后,后台接受到设备的通知,最后,App与后台通信,结束整个流程。
这里需要明确一些问题:
- App <=> Server <=> Device之前的请求与回调都是异步进行的。
- Device的操作是非常耗时了,可能长达2min以上,如何将Device的操作结果有效的反馈到App端,是我们急需处理的问题。
好在,Server <=> Device之前的双向通信,已经在之前就建设好了,数据传递稳定高效,这里我们主要聊的是App <=> Server的方案。
方案一:轮巡
其实这个是我们最先想到,最简单,最暴力的方法。
在App端完成远程控制操作后,我们可以开启一个定时器任务完成一个主动向Server端查询Device操作结果的请求,代码写起来也非常简单,这里只是列出简单的代码例子:
timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true, block: { [weak self] (t) in
guard let strongSelf = self else { return }
/// 网络请求
/// 返回Response,结束轮巡定时器
/// 定时器超时,结束轮巡定时器
})
这种通过定时器做轮巡操作有以下优点:
- 逻辑简单;
- 代码编码容易;
当然缺点也是突出的:
- 定时器可能会有隐患;
- 轮巡,导致每隔一段时间需要进行网络请求,消耗流量同时也消耗性能;
- 用户体验不好,进行轮巡的时候,为了保证定时器在进行任务,用户可能需要在某个页面等待。
所以在我们第一想到轮巡的时候,其实我们基本上就已经否定了这个方法,这种通过App主动向Server端发起请求的方案,并不适合这种场景。
方案二:推送
既然App主动向Server请求结果的方案行不通,那么最好的方案就是由Server主动告之App结果。
推送如何?我们在讨论的过程中,当把方向转成Server主动推消息给App后,我们马上就想到了这个现行的方案:
上面这个图其实是整个iOS的App原理图,其实我们在实际的推送过程中,主要运用的是这张图的下半部分:
Server => APNS => App
考虑一般情况下,我们都会使用第三方推送于是乎链路变成了这样:
Server => Third Server => APNS => App
但是不管怎么变化,本质上还是通过这段代码来接受远程消息:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
print("远程通知")
}
至于使用第三方,无非就是调用第三方SDK的推送回调,这里就不过多描述。
推送这个方案的优势有以下几个:
- 技术成熟,APNS推送精准;
- 编码难度低,只需要约定消息的类型和格式,即可知道是不是Device侧发来的消息,进而进行逻辑判断;
正在我们考虑这种方案的可行性的时候,我们也讨论了其中的一些风险问题:
- Android端的推送体验如何?
- 集成第三方推送会不会出现异常情况?
- 如果需要这个应用需要制作成SDK给其他服务商使用,使用推送好吗?
考虑到我们的这个应用部分可能会给其他App使用,于是乎整个推送链路就变长了:
SDK Server => App Server => Third Server => APNS => App => SDK
我们立即意识到,整条链路越长,调试与沟通的成本就越高,其中任何一个因素都有可能导致消息没有传递到SDK侧。
既然Server主动告之App结果这个思路没有什么错,那么我们就继续沿着这条路扩展。
方案三:Websocket Or MQTT
使用Websocket,进行Server与App的通信,考虑到推送的一些风险之后,我们立刻想到了这个曾经使用过的技术方案。
WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
基本上贯彻了服务端向客户端、主动这个我们非常看重的因素。
同时我们了解了Server和Device两端之间的通讯方式——MQTT。
既然Server目前已有MQTT服务了,那么在Server和App两端再建立Websocket通讯,会显得Server侧复杂,我们想直接统和使用MQTT协议,进行端到端的通讯,此图也说明了,MQTT非常适合我们的这个应用:
我们参考了一些文档与结论:
MQTT和Websocket的主要区别:
1.MQTT不依赖长连接,适合弱网络。通过topic缓存信息。符合物联网设备的使用场景。
2.因为通过topic缓存信息,因此可以实现通过topic与多个端的一对多连接,而不是设备与设备的多对多连接,节省了能耗及带宽。
3.MQTT的心跳,及非信息的报文,较Websocket更少,更节省带宽及能耗。更适用于物理网的多种网络协议。
既然MQTT能够满足我们设想的需求,那么剩下的就是进行初步调试和验证了。
类比和总结
到此,我们的Server与App网络通信初步调研基本完成,可以通过一个表格来进行一些比较和总结:
| 轮巡 | 推送 | MQTT | |
|---|---|---|---|
| 优点 | 逻辑简单,编码容易 | 技术成熟,运用简单 | 主动发消息,适合弱网络,节省带宽及能耗 |
| 缺点 | 方式粗暴,体验不好 | 依赖第三方服务,链路过长时,排查与沟通成本较多 | 目前没有在App端使用,需要进行前期的调试,考虑该技术已经在很多方面应用,风险应该不大 |
这次调研,我们先从最常见的网络请求开始,作为相互通信的起点,然后发现通过http协议的方式并不能完成使用场景。
然后,将App主动向Server的思路抛弃,进而考虑Server向App主动通信,并考虑了现成的方式,推送,接着我们考虑在应用方面的一些特殊场景,并没去采纳。
最后,秉持Server向App主动通信的思路,我们考虑的Websocket,同时考虑现有的Server和Device的通信方式,我们将MQTT协议作为了Server与App网络通信的方式。
下一节,我会讲解一下MQTT的一些简单调试。