推送是在日常终端使用场景中经常碰到,特别是移动互联网普及之后,手机终端成为了消息推送的主战场,例如生活服务类的优惠券推送,咨询类的新闻推送,电商类的购物推送等等,在业务用户触达上起到了至关重要的作用,那我们今天就来揭开一下推送这个隐藏在业务背景之下的技术实现
系统架构及模块介绍
这是一个比较完整的推送业务架构图,分为三个部分:业务层、通道层和客户端常驻服务,一般来说客户端常驻服务和通道层维持一个长连接通道实现数据的双向传递,而业务层实现的是基于推送业务形态的展示,例如推送的定时任务推送,接口推送,以及消息类型定义等等,我们这篇文章先聚焦通道层和客户端常驻服务的一个组件介绍和关键技术实现。
通道层
推送后端接入:这一层是业务的接入层,一般来说是对内网开放,通常采取的是RPC的远程调用实现,消息更高。
存储:依赖进行消息数据统计,以及离线消息信息存储,待终端网络开启之后再实施推送。
消息分发:依据消息到达进行就近机房选择,消息体封装等。
推送前端接入:和客户端进行长连接维持。
客户端
鉴权及防伪服务:进行消息体格式校验,消息防伪造验证
状态适配服务:识别当前终端所处环境和状态,例如微信所做的状态适配服务区分为:活跃态、次活跃态、自适应计算态、后台稳定态以及空闲态等几种。选择不同的状态会传送给心跳服务采取不同的心跳时间间隔。
心跳服务:为了应对NAT断连、DHCP租期失效、连接探测,需要有一个心跳服务进行维持,而心跳服务的选择策略是长连接维持的一个重要优化点。
后端感知:主要是为了应对DNS劫持以及就近流量访问所出现的一个服务。
对一个推送的基本架构和业务模块有一个初步了解之后我们可以接下来谈一谈关于实现这个系统的几个关键技术难点了
心跳机制优化
前面我们已经讨论了长连接里面一个非常重要的优化点就是心跳机制优化,那位什么需要心跳机制优化,我们先看下现实场景下有什么问题会导致一定需要长连接维持及优化。长连接需要维持那么肯定是有一些原因会导致长连接失效,总结一下有如下几个场景:
NAT断连
因为IP v4的IP量有限,运营商分配给手机终端的IP是运营商内网的IP,手机要连接Internet,就需要通过运营商的网关做一个网络地址转换(Network Address Translation,NAT)。简单的说运营商的网关需要维护一个外网IP、端口到内网IP、端口的对应关系,以确保内网的手机可以跟Internet的服务器通讯。大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰NAT表中的对应项,造成链路中断。下表列出一些已测试过的网络的NAT超时时间(更多数据由于测试条件所限没有测到):
长连接心跳间隔必须要小于NAT超时时间(aging-time),如果超过aging-time不做心跳,TCP长连接链路就会中断,Server就无法发送Push给手机,只能等到客户端下次心跳失败后,重建连接才能取到消息。
安卓DHCP的租期(lease time)问题
目前测试发现安卓系统对DHCP的处理有Bug:DHCP租期到了不会主动续约并且会继续使用过期IP,详细描述见http://www.net.princeton.edu/android/android-stops-renewing-lease-keeps-using-IP-address-11236.html。这个问题导致的问题表象是,在超过租期的某个时间点(没有规律)会导致IP过期,老的TCP连接不能正常收发数据。并且系统没有网络变化事件,只有等应用判断主动建立新的TCP连接才引起安卓设备重新向DHCP Server申请IP租用。
流程优化
基于以上两种因素的考虑,长连接就很有可能会出现断开,那么具体选择多久进行一次心跳探测呢?首先可以肯定的一点不能太久,要不能连接早已被断开或者说消息接收不及时,那太频繁的进行心跳检测呢显然也有一个问题,那就是功耗会加大,如何平衡这个选择呢?通常做法就是依据状态选择智能选择心跳时隔,这里的状态分为两类:网络状态及应用所处场景状态(例如前面所描述的微信所区分的几类状态)。
1、按场景状态选择心跳区间
2、通过心跳增加步长(网络质量良好)和心跳减少步长(网络质量差)来逐渐逼近网络最优心跳区间
心跳区间选择流程如下
消息重复接收问题优化
如果消息是单次发送及反馈,那么在网络条件不好的时候很容易出现消息重复接收问题,而如何解决消息重复接收呢?
可以通过消息序列标注解决法,具体的做法是发一批带序列号的消息下发,客户端定期反馈当前接收到的消息序列,如果序列和当前发送不匹配就从反馈的序列号开始重新发送。
消息协议选择
首先需要确认的是这个协议是一个基于4层之上的应用层协议,在4层还是采取的是TCP/IP协议。那么我们来看下这个应用层协议如何选择
文本协议:XMPP(xml)、SIP(类http协议)
这些协议有如下共同特征,可读性强、开源组件多、协议较复杂、冗余、费流量。
二进制数据协议:protocol
buffer(PB)、MQTT
这些协议的特征是:可读性差,流程可自定义(MQTT自定义限制较大)、协议简单,轻量、编解码速度快,可大量节约流量,测试可节约50%~70%的流量
所以具体那种协议的选择需要看业务场景的使用而定。
DNS劫持及就近流量接入
这个就涉及我们刚才看到的客户端出现的后端感知服务的功能,一般来说可以通过IP来和后端服务建立长连接,一旦DNS劫持之后IP连接不通,那么就可以在服务端维护一个后端服务IP列表,在客户端通过hash散列的方式去随机挑选其中一个IP连接,如果发现不通更新这个IP列表信息,这个功能还可以实现流量就近接入,例如通过把所有IP测试一遍,选择连接效率最高的那个IP就行建立长连接。
鉴权防伪
当一个恶意APP伪造透传消息内容体来拉起业务行为并打开恶意网站就有可能导致用户信息泄露,那么这里就有涉及一个消息防伪问题,一般做法就是通过消息MD5加签同时进行可逆加密,到达客户端之后进行反向解密校验就可防止消息伪造的行为。
感兴趣的童鞋可以加微信公众号共同探讨:Fei_Talk