众所周知,IM 即时通讯是一项对即时性要求非常高的技术,而保障消息即时到达的首要条件就是链路存活。那么在复杂的网络环境和国内安卓手机被深度定制化的条件下,如何保障链路存活呢?本文详解了融云安卓端 SDK 在基于 TCP 协议实现链路保活方面的探索和经验。

IM 系统整体框架

如上图所示,为了保障链路存活,一套成熟的 IM 系统一般会包含消息链路和推送链路两条长连接通道。当有新消息到达时,消息服务首先会判断消息链路是否存活,如果消息链路处于存活状态,消息优先从消息链路下发到客户端,否则会被路由到推送服务器,由推送链路下发。

综上所述,链路保活涉及到消息链路和推送链路两条链路的保活策略。基于这两条链路使用场景的不同,保活策略上除了心跳机制是相同的,其它保活策略各有不同。下面将详细讲解。

链路保活的必要性

基于 TCP 的 Socket 连接建立之后,如果不做任何处理,这个连接会长时间存在并且可用吗?答案是否定的。原因有两点:

一、默认 Socket 连接无法及时探测到链路的异常情况,即使将 Socket 的属性参数 keepAlive 设置为 true 仍然无法及时获取到链路存活状态。这是因为 Socket 的连接状态是由一个状态机进行维护的,连接完毕后,双方都会处于建立状态。假如某台服务器因为某些原因导致负载超高,无法及时响应业务请求,这时 TCP 探测到的仍然是连接状态,而实际上此链路已经不可用了。

二、国内运营商的 NAT 超时机制会把一定时间内没有数据交互的连接断开,这个时间可能只有几分钟,远无法满足我们的长连接需求。

通用保活机制 - 心跳机制

基于以上原因,要维持 Socket 连接长时间存活,就需要实现自己的保活机制。最通用的一种保活机制就是心跳机制。即客户端每隔一段时间给服务器发送一个很小的数据包,根据能否收到服务器的响应来判断链路的可用性。为了节省流量,这个包一般非常小,甚至没有内容。

那么客户端如何实现定时发送心跳包呢?一般有两种方式:

一种是通过 Java 里的 Timer 来实现。最基本的步骤如下:

1、建立一个要执行的任务TimerTask。

2、创建一个Timer实例,通过Timer提供的schedule()方法,将 TimerTask 加入到定时器Timer 中,设置每隔一段时间执行 TimerTask , 在 TimerTask 里发送心跳包。这种方式实现起来较简单,而且省电,不需要持有 WakeLock 。缺点也很明显,长时间在后台,进程被回收或者系统休眠后, Timer 机制随之失效。

另外一种方式是利用安卓系统的定时任务管理器 AlarmManager 循环执行发送心跳包的任务。这种方式不会因为系统休眠而失效,系统休眠后仍然可以通过 WakeLock 唤醒,执行心跳任务,因此相对 Timer 机制,这种方式比较费电,使用的时候一定要注意如下几点:

首先根据需求合理使用 AlarmManager 的闹钟参数。闹钟各参数的区别参考下表:

其次 AlarmManager 提供了 cancel() 方法,在设置新的定时任务前,通过 cancel() 方法取消系统里设置的同类型任务,避免设置冗余任务。

最后,安卓从 6.0 版本引入了 Doze 模式,并提供了新的闹钟设置方法 setExactAndAllowWhileIdle(),通过该方法设置的闹钟时间,系统会智能调度,将各个应用设置的事务统一在一次唤醒中处理,以达到省电的目的。推荐在安卓 6.0 以上系统中,优先使用该方法。

消息链路保活机制

消息链路作为收发消息的主要通道,需要最大程度保障链路的可用性。在链路不可用或者异常断开时,能及时探测并启动重连等保障机制。基于以上特性,消息链路除了前面所说的心跳机制外,还另外维护了两套链路优化机制:复合连接机制和重连机制。

复合连接机制的基本步骤如下:

1. 客户端连接导航服务器,导航服务器会下发应用对应的配置信息,其中包括连接服务器的地址列表。

2. 客户端从第一个服务器地址尝试连接,并启动超时机制,如果连接失败或没有及时收到服务响应, 则继续尝试连接下一个直到成功连接,将成功连接的地址保存到本地,作为最优地址,后面连接时优先使用此地址。通过这种机制,能保障客户端优先选用最优链路,缩短连接时间。

重连机制,则是指业务层在检测到与服务器的连接断开后,尝试 N 次重新连接服务器,首次断开 1 秒后会重新连接,如果仍然连接不成功,会在 2 秒后(重连间隔时间为上次重连间隔时间乘 2 )尝试重新连接服务器,以此类推当尝试重连 N 次后,仍然连不上服务器将不再尝试重新连接,只有在网络情况发生变化或重新打开应用时才会再次尝试重连。

推送链路保活机制

推送链路作为消息到达的补充手段,要求尽可能延长在后台的存活时间。即使被杀后,仍然能被再次唤醒。iOS 手机有 APNS 来达到以上效果,但安卓的官方推送系统 FCM 在国内基本不可用。那在国内安卓系统上如何保障推送到达呢?首先咱们需要先了解下安卓系统上进程管理的两大机制:

一种是 LMK 机制,英文是 Low Memory Killer, 基于 Linux 的内存管理机制衍生而来。主要是通过进程的 oom_adj 值来判定进程的重要程度,从而决定是否回收这些进程。oom_adj 的值越低,代表重要度越高,比如 native 进程,framework 层启动的系统进程,优先级一般都为负数。其次是前台可见进程,系统也不会回收。然而可见进程退到后台后, oom_adj 的值会立即升高,在系统定时清理时被杀。

另外一种机制是安卓原生的权限管理机制(AppOps),各大厂家在此基础上又进行了深度定制化,比如小米的安全中心,华为的手机管家等,都用来进行权限管理。该权限管理机制运行在安卓系统的框架层,上层各应用的进程如果想尝试重新启动,系统首先会去权限管理中心检查该进程有没有自启动权限,如果有,才准予启动。否则,从框架层直接限制系统的启动。

基于以上两种机制,推送链路的保活也可分为两大类,

一 进程保活。它的思路是根据 LMK 机制提高进程优先级,降低被杀的几率。主要有以下几种方法:

监听黑屏事件,启动 1 像素透明 Activity ,使应用进程转为可视进程,降低被杀概率。在屏幕亮时,关闭该 Activity。

双服务守护。A 服务以 startForeground() 形式启动,发送一个通知,B 服务同样以 startForeground() 形式启动,且发送和 A 相同 ID 的通知,然后在 B 服务里调用 stopForeground() 方法,取消通知。这样 A 服务就会以前台进程的形式存活,且不影响用户感知。

根据文件锁互斥原理,监视 Java 进程存活状态,若被杀,Linux 层成功持有文件,则通过 exec() 命令,打开一个纯 Linux 的可执行文件,开启一个 Daemon 进程, 该进程因为从 Linux 层启动,在安卓 5.0 之前,优先级会比较高,不会被杀。在安卓 5.0 之后,该方式不再有效。

二 进程拉活的策略和安卓系统的 AppOps 机制有关,一般有如下几种:

一、利用 Service 本身的 Sticky 属性,在 Service 的 onStartCommand() 中返回START_STICKY,这样当 Service 被杀掉后,系统会自动尝试重启。不过在国内定制化的系统上,这种方式能成功重启的几率很低,需要用户在权限管理中心打开自启动等权限,才能成功拉活。

二、也就是前面讲过的心跳机制,不过这里要求使用 AlarmManager 设置 ELAPSED_REALTIME_WAKEUP 属性的闹钟,在系统休眠后,才会正常接受到心跳事件,从而将进程拉活。

三、通过监听网络切换,用户行为等事件,拉起进程。

四、应用间互相拉活。比如系统里有好几个应用集成了同一个 SDK , 那么在用户启动其中某一个 App 的时候,SDK 会去扫描其它应用,把"兄弟姐妹" 拉活。这种方式对用户体验伤害非常大,会造成系统莫名其妙的耗电。

随着安卓系统版本的迭代,对后台进程的启动管控越来越严。为了解决推送的问题,各手机厂家推出了自己的系统级推送服务。由厂家在 Framework 层统一维护一条推送通道,上层所有应用共同使用该推送链路,不需要再维护单独进程。当前支持系统级推送的厂家有:小米、华为、魅族、vivo、OPPO,这种系统级别的推送省电,省内存,到达率高。应用可以根据手机型号的不同,优先使用厂家系统级别的推送,再配合自身的保活机制,最大程度保障推送的到达率。

集成第三方系统级推送之后,整个消息的收发流程可以参考下图:

【融云分析】 IM 即时通讯之链路保活的更多相关文章

  1. APICloud框架——融云+UIChatTools实现即时通讯聊天

    今天完成了公司app的聊天界面的收发消息功能,结合融云2和UIChatTools模块实现,只是实现了基本功能,好多细节还没有实现,废话不多说,上代码 输入框页面(win) 先引入所需模块 // 融云模 ...

  2. 【融云分析】如何实现分布式场景下唯一 ID 生成?

    ◀背景▶ 对于一套分布式部署的 IM 系统,要求每条消息的 ID 要保证在集群中全局唯一且按生成时间有序排列.如何快速高效的生成消息数据的唯一 ID ,是影响系统吞吐量的关键因素.那么,融云是如何做到 ...

  3. 融云技术分享:融云安卓端IM产品的网络链路保活技术实践

    本文来自融云技术团队原创分享,原文发布于“ 融云全球互联网通信云”公众号,原题<IM 即时通讯之链路保活>,即时通讯网收录时有部分改动. 1.引言 众所周知,IM 即时通讯是一项对即时性要 ...

  4. 融云亮相GDG谷歌女性开发者大会 揭秘IMSDK网络优化策略

    4 月 20 日,冷雨阻碍不了天津GDG谷歌女性开发者大会的热烈召开,一众开发者.架构师和科技公司创业者云集一堂,就女性开发者的技术.职场.人生多方面话题展开深入探讨.活动由GDG (谷歌开发者社区) ...

  5. 融云通信云发力教育行业 助在线教育"风口"继续腾云

    4 月 16 日,2019 年AI 在线教育大会在北京站圆满落幕,会上云集超过500位资深教育从业者.200 家机构,共同就 AI 教育落地.在线教育应用经验等主题开展深入探讨.云通信领域的领导企业融 ...

  6. 融云SDK触达用户数破20亿 王者风范双倍展现

    11月1日,融云SDK触达用户数突破20亿,业务增长速度及用户覆盖量再创即时通讯云领域新高.自去年11月10日公布SDK触达用户数破10亿以来,融云仅用了一年时间,便取得了触达用户数翻倍的成绩,迅猛的 ...

  7. IM云服务领域,融云因何得以登上浪潮之巅?

    每一次技术驱动下的商业变革都是以浪潮的形式到来,每一次的涨潮都会带着赶上浪潮的企业登上新的巅峰.随着移动互联网的融合发展,短短几年期间,IM通讯云服务从早期的虚无飘渺到如今已经成为现代企业的标配,引发 ...

  8. 融云携新版实时音视频亮相 LiveVideoStack 2019

    4 月 19 日,LiveVideoStack 2019 音视频大会在上海隆重开幕,全球多媒体创新专家.音视频技术工程师.产品负责人.高端行业用户等共襄盛会,聚焦音频.视频.图像.AI 等技术的最新探 ...

  9. 融云红包全新升级,让App用户更便捷地用“钱”交流感情!

    随着移动互联网的飞速发展,如何增强社交关系.留住用户的心已成为移动社交化时代各类App持续探索的问题,除了接入即时通讯的能力,众多社交平台开始通过趣味性十足的红包功能为App中的社交场景赋能.当即时通 ...

随机推荐

  1. leetcode 单链表相关题目汇总

      leetcode-19-Remove Nth From End of List—移除链表中倒数第n个元素 leetcode-21-Merge Two Sorted Lists—两个已排序链表归并 ...

  2. NTSC PAL 介绍

    NTSC-J是日本地区的模拟 电视系统和视频显示标准,于2011年7月24日在全国47个县中的44个地区停止运营.模拟广播于2012年3月31日在2011年Tōhoku摧毁的三个县停止地震和海啸(岩手 ...

  3. GParted: GNOME Partition Editor, sharp weapon to modify disk partitions.

    GParted Projects GNOME Partition Editor for creating, reorganizing, and deleting disk partitions. It ...

  4. Overview of .rdp file settings

    On this page you will find an overview of most of the available .rdp file settings which can be used ...

  5. Python 学习笔记4 变量-字符串

    Python中的字符串,我们可以简单的认为是一组用单引号,双引号,三引号包含的一组字符,数字或者特殊字符.在Python3中,所有的字符串都是Unicode字符串. 变量定义 #单引号 string1 ...

  6. Oracle 表空间恢复

    为啥要写这个呢,因为之前遇到个场景.操作系统为Solaris的,oracle11.2.0.4. 一个运维把一张关键表drop了.然后发现recyclebin是off的,然后..然后好像只能从备份里面找 ...

  7. 一、使用官方工具建立空springboot

          自己搭过springboot,看的官网,一点点自己弄,集成druid,做了些例子,从0到1弄了一下午. 当时没看到有工具可用,可以把依赖都加上,简称STS.       下载地址: htt ...

  8. Windows命令行设置dns

    管理员运行cmd 1.dns配置成192.168.1.200 netsh interface ip set dns name="以太网" source=static addr=19 ...

  9. Logstash - Working with plugins(使用插件)

    本章节开始介绍logstash的插件及功能,插件对于logstash来说非常重要,按类别分为:input.filter.codec.output四种类型. logstash有非常丰富的插件,通过安装目 ...

  10. rpm和yum模拟安装

    在更新安装包之前,我们可能会想做一个测试运行,换句话说,模拟而不是实际安装更新的包,以确定在安装之前是否有任何需要处理的问题. 以测试更新openssh2为例: yum update openssh2 ...