App采用的Websocket长连接和服务器通信,App切到后台3秒就被断网,如何解决?公司的App用来智能家居控制的,控制的成功率是易用的关键。目前用户反馈两个问题:App打开时第一次控制不成功;有时连续多次控制不成功,需要重启App后才能正常。

如何调试

App切到后台几秒后,和服务器之间的心跳报文打印没有了,这是就是系统搞得鬼。想要观察到系统对App切到后台时断网现象,需要使用WiFi ADB代替USB进行调试。因为USB连接着的时候属于充电状态,充电时后台断网不会出现,当使用WiFi ADB的时候可以模拟用户真实使用手机的情况了。

硬碰硬

所谓硬碰硬就是保证socket永远不断开,切到后台时也不断开。但是实际情况是如果App不加入系统白名单,就很难在后台长连接不断开(有的手机上术语叫后台使用数据)。断开也很有意思,是半断开的情况,你无法感知到Socket已经断开。切到前台时系统会自动再将半断开的Socket再连上App还是无感。这种现象很像现实社会中的一个现象,一时说不上来。

另外网上也有很多例子,我这里不再赘述。 有帮助的截图

尝试过的方案:

  1. 启动线程检测心跳超时后直接重新连接
    结果是检测线程也会被一起冻结
  2. 各种保活方案
    没能实现
  3. 添加到白名单
    领导不愿意强迫用户手动将App加入白名单,所以和系统硬碰硬方案失败。

打太极

在硬碰硬的时候发现,其实友盟的长连接也是在切后台的时候断开,切前台时重连。刚好我遇到的问题还只是第一次不能控制问题,用户并不需要后台的时候控制。只需要在App切到前台的时候,快速恢复网络就可以了。在Activity的生命周期的地方对Socket进行了控制,切后台时自动断开,切到前台时自动连接上。方案推出之后,又得到一个反馈,仍然有第一次不能控的问题且还有连续控制无效的问题。查看日志后得知切前台时0.5秒能连接成功,但是用户手快在0.4秒就控制了,还是控制不成功;另外一种情况是永远处于连接中状态,消息就会一直发不出去。接下来的路就是继续强化长连接连接更快和更稳,或者在WS不通的时候改用HTTP补救。前者比较难查,后者服务器改动比较多。两个方案都比较棘手。

如果我安排采用WS不通的时候改用HTTP补救方案,同事们就会开始轰轰烈烈动起来,没有一个人反对,但是站在社会工程学角度来看,这也不好。我要反思我的决定是不是正确,因为做错一个决定就会导致架构方向偏离。

还有一个太极方案,网络恢复成功之前不让用户操作,我没采纳。在onPause 和 onResume 时候分别设置状态然用户不要交互就好。真要这样做的只要在整个屏幕上加一个loading吗?我觉得丑爆了。反而像微信一样,他只在“第一个tab的标题上”显示了网络状态,在具体的聊天窗口,你根本不知道网络状况。因为你不知道用户在那个界面,所以我不赞同,这个问题我站在了张小龙一方。

微信Mars框架

有想过上国民App微信开源的Mars框架,但是现在已经积重难返。

最终结果

这次又应对了我总结的理论,只要下功夫把问题描述清楚,丢到互联网上,答案就自动出现。通过写本文的过程,我又将流程重新梳理了一下。对于WebSocket的使用上并没有太好。因为结合的状态机分为Init,Auth,Communication等三种大状态。重要命令有CMD_DISCONNECTED,发这个命令地方有网络断开切后台。在Init的Enter方法中并调用了WebSocket中的reconnect,但是其实并没有任何效果。让reconnect中强制调用了stopSocket,然后再调用connect这样就保证了调用稳定性。这样就解决了稳定性的问题。还好结合社会工程学没有把问题搞得更坏。这里适合画个流程图出来,如下:

有帮助的截图

网友建议,但是未测试的方案

其实基础架构都用了WebSocket不如进一步用那些更完善的无中继消息库..比如zeromq或者nng之类的,它们有内建的架构pattern、排队重发、心跳等特性,可以减少对网络这种奇怪事情的管理。比如说这个case,如果有排队自动重发的话根本不存在,当然你也可以自己实现一下这个机制。