接系列文章
① 就快接入
在客户端接入服务器调度策略的演化过程中,我们最早采用了“就近接入”的策略,在距离客户端更近的地方部署服务器或使用CDN,期望通过减少RTT来提高网络交互响应性能。这个策略在国内的落地执行还需要加一个前缀:“分省分运营商”,这就给广大负责IDC建设的同学带来了巨大的精神和肉体折磨。
在持续运营的过程中,根据观察到的数据,发现并非物理距离最近的就是最快的。回忆一下前面谈到的吞吐量指标BDP,它与链路带宽和RTT成正比关系,而RTT是受物理距离、网络拥塞程度、IDC吞吐量、跨网时延等诸多因素综合影响的,单纯的就近显然不够精细了。
“就快接入”在“就近接入”策略的基础上改善提升,它利用客户端测速和报告机制,通过后台大数据分析,形成与客户端接入IP按就快原则匹配接入服务器的经验调度策略库,令客户端总能优先选择到最快的服务器接入点。
对于接入服务器,我们按照访问目标数据属性纬度的不同,可以分为至少两个集合,它们分别是:
1)业务逻辑服务器集合;
2)富媒体服务器集合,富媒体包括头像、图片和视频等尺寸比较大的数据;
这两类服务器集合通常由独立的接入调度FSM管理。
客户端在访问不同的数据类型时使用不同的服务器集合,这样的划分体现了轻重分离、信令和数据分离的架构理念。
每个服务器集合又可按接入调度的优先秩序划分为三个子列表:
1) 【动态服务器列表】
服务器按策略(比如就快接入)并结合设备负载情和容量情况、网络容量情况综合计算下发的一系列服务器IP地址,某些产品还会在动态服务器列表靠后的部分加上动态服务器域名(该域名与静态服务器域名列表内容不同,是一种动态扩展方式),对于下载类业务,动态服务器列表最后会包含动态回源服务器IP地址等。客户端应当持久化存储动态服务器列表,并在APP启动时加载到内存缓存中,其缓存索引的KEY通常是网络类型,对于WIFI网络,KEY的内容中再加上一个SSID,以便区分不同的WIFI热点。客户端在持久化和内存中基于不同的KEY缓存3 ~ 5组(建议值,可根据业务特点灵活选择和配置)动态服务器列表数据,并按照LRU方式做更新淘汰;
2) 【静态服务器域名列表】
预埋在客户端持久化存储中,在首次启动APP或动态服务器列表访问全部失败时使用;
3) 【静态服务器IP列表】
预埋在客户端持久化存储中,其主要价值在于,当使用客户端遇到动态服务器列表和静态服务器域名列表访问都出现异常时,有最低限度的可用性保障。静态服务器IP列表贵精不贵多,能分别服务国内和海外用户即可。对于下载类业务,静态服务器IP列表最后还有包含静态回源服务器IP地址;
每个服务器列表都包含一批列表项,一般为2 ~ 3个。每个服务器列表中的列表项按照优先顺序从前到后排列,故也需维护一个自己独立的调度机制,我们称之为服务器列表调度FSM。
基于以上的分类基础,客户端和服务器接入调度机制的具体的做法通常为:
1) 客户端实现接入调度FSM模型和服务器列表调度FSM模型,这两个FSM是嵌套关系,可以理解为外循环和内循环的关系,就好比地球围着太阳公转的时候也没耽误自转;
2) 客户端存储预埋业务逻辑和富媒体两个服务器集合,每个服务器集合都包含静态服务器域名列表和静态服务器IP列表;
3) 服务器实现就快接入调度算法,依托异步计算持续更新的经验调度策略库,进行动态匹配计算;
4) 客户端和服务器共同实现一套动态服务器列表下发和更新机制;
5) 实践中有些服务器还要求客户端支持302跳转的能力,这个逻辑机制上可以有,策略上不提倡;
我们先考察接入调度FSM,如【图十九 接入调度FSM示意】,它的状态变迁驱动力来自:
1) 当前状态下相应的服务器列表无有效数据(数据项为空或全部试完一轮);
2) 服务器下发了新的动态服务器列表;
接入调度FSM状态变迁的原则是:
1) 客户端首次使用时,接入调度FSM状态入口在静态服务器域名列表;
2) 客户端在冷启动(除首次使用)、热启动时,接入调度FSM状态入口在动态服务器列表。动态服务器列表通常在冷启动时从本地持久化缓存加载,在内存缓存中会被服务器下发的数据更新,一旦更新,客户端应择机持久化到本地存储中;
3) 接入调度FSM状态变迁时,以进入服务器下发的动态服务器列表状态为最高优先级,即三个服务器列表发生状态变迁时,都先向服务器动态列表跳转;
4) 第3点之特例:当刚从动态服务器列表变迁到静态服务器域名列表且未收到服务器下发新的动态服务器列表时,静态服务器域名列表变迁的下一站是静态服务器IP列表。这里要特别谈一下前面那个时间限定词“刚”,这个前提设定的原因是移动网络易抖动,1分钟前动态服务器服务器列表不可用不代表5分钟后依然不可用,因此,我们把这个“刚”设定为:一直在前台运行的5分钟以内的时间;
5) 特别的,如果是因为服务器下发新的动态服务器列表导致状态变迁,那么接入调度FSM状态要置位还原,重新按第2条原则执行;
【图十九 接入调度FSM示意】
我们以动态服务器列表为例来考察服务器列表调度FSM,先说明一下,同其他两个列表不同的是,动态服务器列表中的列表项数量完全由服务器下发时控制。如【图二十 动态服务器列表调度FSM】所示,服务器列表调度FSM的状态变迁驱动力来自:
1) 链接建立失败或超时;
2) 链接建立成功但收发数据错误(包含网络类型切换、无网络等)或超时;
3) 服务器下发新的动态服务器列表;
服务器列表调度FSM状态变迁的原则为:
1) APP冷启动时,服务器列表调度FSM状态全部重新置位,按第2条原则 执行;
2) 客户端由前到后顺序尝试服务器列表中的数据项,不可逆向执行、不可乱序执行;
3) 客户端尝试一遍本服务器列表所有数据项,如果全部失败,则退出这个服务器列表调度FSM,进入到接入调度FSM;
4) 链接建立失败(建议要再做1 ~ 2次重试,重试间隔3 ~ 5s,这两个参数 云端应该可配可控,相关详细讨论可参考3.1.3链路管理)或超时、链接 建立成功收发数据错误或超时、服务器下发新的动态服务器列表时,服务 器列表调度FSM状态要变迁;
5) 特别的,如果是因为服务器下发新的动态服务器列表导致状态变迁,那么服务器列表调度FSM状态要置位还原,重新按第2条原则执行;
【图二十 动态服务器列表调度FSM】
客户端接入调度首要目标是确保可用性,其次是选择最快的链路。客户端无论同哪个集合中哪个服务器列表的接入服务器建立链接,服务器都应按照就快策略的标准评判此时客户端选择的服务器接入点是否符合要求,有没有更快的接入点,如果有,就随着业务数据响应一并下发至客户端,客户端同步更新动态服务器列表的数据,驱动调度FSM和服务器列表调度FSM发生状态变迁,使得下次再发起服务器访问时能使用更优的接入服务,接入链路切换时机这里有三个方案可供探讨(后续链路管理也会有相关的讨论):
1) 直接关闭当前链路,立即尝试使用新的动态服务器列表建立链接;
2) 直接关闭当前链路,当有网络访问时尝试使用新的动态服务器列表建立链接;
3) 保持当前链路,立即尝试使用新的动态服务器列表建立链接,一旦成功,马上切换新的业务请求到新链路上,然后在旧链路空闲时将其关闭;
实践中可以根据APP的特点来选择链路切换方案。
那么,客户端报告什么样的数据可以作为服务器调度策略计算的依据呢?
1) 网络类型,比如WIFI、2/3/4G等,WIFI时多提供一份SSID信息;
2) 接入IP归属,比如电信、联通、移动、海外及其所属省市等,注:归属由服务器判断;
3) 目标域名,用于服务端校验访问目标和自己提供的服务是否匹配;
4) 访问目标服务时的测速数据(IO次数、每次IO字节和耗时、RTT估算值等)和服务质量数据(如接入调度FSM状态、服务器地址、链接成功或失败、链接成功所需时长、链接失败错误码、重试次数等);
说了半天,这一切的基础是我们要部署足够多和广的服务器接入点,也可以使用CDN,依托在一个分省市分运营商甚至覆盖全球的IP库和通过大量客户端测速报告的业务质量统计数据计算出来的、接入IP按就快原则匹配接入服务器的经验调度策略库之上。
总结一下与就快接入相关的内容:
1) 服务器分省分运营商分国内外的部署及使用CDN,广度和深度并举;
2) 客户端测速报告及服务质量监控报告,测速这个话题,稍微多探讨一下,在有 线网络,实时测速并调整调度策略数据是非常普通的方案,但放在移动网络条件下,就有重新思考的必要。移动网络易抖动和移动应用大部分短链接轻量交互的特点,使得我们很难在一个短的时间内做出网络速度的有效判断,即便有初步的判断,也可能因为没有马上使用的时机而导致过期失效。因此,我们更倾向于把这些质量数据报告到后台,通过大量的数据归并分析,形成接入速度调度策略的判断依据;
3) 客户端接入IP库与接入服务器就快调度匹配库需要持续更新;
4) 服务器调度中尽量减少302跳转,做到一击即中;
② 去DNS的IP直连:
DNS不但需要1个RTT的时间消耗,而且移动网络下的DNS还存在很多其它问题:
1) 部分DNS承载全网用户40%以上的查询请求,负载重,一旦故障,影响巨大,这样的案例在PC互联网也有很多,Google一下即可感受触目惊心的效果;
2) 山寨、水货、刷ROM等移动设备的LOCAL DNS设置错误;
3) 终端DNS解析滥用,导致解析成功率低;
4) 某些运营商DNS有域名劫持问题,实际上有线ISP也存在类似问题。域名劫持对安全危害极大,产品设计时要注意服务端返回数据的安全校验(如果协议已经建立在安全通道上时则不用考虑,安全通道可以基于HTTPS或者私有安全体系)。对于劫持的判断需要客户端报告实际拉取服务数据的目标地址IP等信息;
5) DNS污染、老化、脆弱;
综上就是在前述就快接入小节中,接入调度FSM会优先使用动态服务器列表的原因。
③ 网络可达性探测
在连接建立过程中如果出现连接失败的现象,而终端系统提供的网络状态接口反馈网络可用时,我们需要做网络可达性探测(即向预埋的URL或者IP地址发起连接尝试),以区别网络异常和接入服务异常的情况,为定位问题,优化后台接入调度做数据支持。
探测数据可以异步报告到服务器,至少应该包含以下字段:
1) 探测事件ID,要求全局唯一不重复;
2) 探测发生时间;
3) 探测发生时网络类型和其它网络信息(比如WIFI时的SSID等);
4) 本地调度的接入服务器集合类型;
5) 本地调度的接入服务器IP(如使用域名接入,可忽略);
6) 探测的目标URL或IP地址
7) 本次探测的耗时;
链路就是运肥猪的高速路,就快接入是选路,链路管理就是如何高效的使用这条路。下面是一些实践总结:
① 链路复用
我们在开篇讨论无线网络为什么慢的时候,提到了链接建立时三次握手的成本,在无线网络高时延、频抖动、窄带宽的环境下,用户使用趋于碎片化、高频度,且请求响应又一次性往返居多、较频繁发起等特征,建链成本显得尤其显著。
因此,我们建议在链路创建后可以保持一段时间,比如HTTP短链接可以通过HTTP Keep-Alive,私有协议可以通过心跳等方式来保持链路。具体要点建议如下:
1) 链路复用时,如果服务端按就快策略机制下发了新的接入动态服务器列表,则应该按照接入调度FSM的状态变迁,在本次交互数据完成后,重建与新的接入服务器的IP链路,有三个切换方案和时机可选择:
a. 关闭原有链接,暂停网络通讯,同时开始建立与新接入服务器的TCP链路,成功后恢复与服务器的网络交互;
b. 关闭原有链接,暂停网络通讯,待有网络交互需求时开始建立与新接入服务器的IP链路;
c. 原有链接继续工作,并同时开始建立与新接入服务器的TCP链路,成功后新的请求切换到新建链路上,这个方式或可称为预建链接,原链接在空闲时关闭;
2) 链路复用时区分轻重数据通道,对于业务逻辑等相关的信令类轻数据通道建议复用,对于富媒体拉取等重数据通道就不必了;
3) 链路复用时,如与协议合并(后面会讨论)结合使用,效果更佳;
② 区分网络类型的超时管理
在不同的网络类型时,我们的链路超时管理要做精细化的区别对待。链路管理中共有三类超时,分别是连接超时、IO超时和任务超时。我们有一些经验建议,提出来共同探讨:
1) 连接超时:2G/3G/4G下5 ~ 10秒,WIFI下5秒(给TCP三次握手留下1次超时重传的机会,可以研究一下《TCP/IP详解 卷一:协议》中TC P的超时与重传部分);
2) IO超时:2G/3G/4G下15 ~ 20秒(无线网络不稳定,给抖动留下必要的恢复和超时重传时间),WIFI下15秒(1个MSL);
3) 任务超时:根据业务特征不同而差异化处理,总的原则是前端面向用户交互界 面的任务超时要短一些(尽量控制在30秒内并有及时的反馈),后台任务可以长一些,轻数据可以短一些,重数据可以长一些;
4) 超时总是伴随着重试,我们要谨慎小心的重试,后面会讨论;
超时时间宜短不宜长,在一个合理的时间内令当前链路因超时失效,从而驱动调度FSM状态的快速变迁,效率要比痴痴的等待高得多,同时,在用户侧也能得到一个较好的正反馈。
各类超时参数最好能做到云端可配可控。
③ 优质网络下的并发链路
当我们在4G、WIFI(要区分是WIFI路由器还是手机热点)等网络条件较优时,对于请求队列积压任务较多或者有重数据(富媒体等下载类数据)请求时,可以考虑并发多个链路并行执行。
对于单一重数据任务的多链接并发协同而言,需要服务器支持断点续传,客户端支持任务协同调度;
④ 轻重链路分离
轻重链路分离,也可以说是信令和数据分离,目的是隔离网络通讯的过程,避免重数据通讯延迟而阻塞了轻数据的交互。在用户角度看来就是信息在异步加载,控制指令响应反馈及时。
移动端大部分都是HTTP短链接模式工作,轻重数据的目标URL本身就不同,比较天然的可以达到分离的要求,但是还是要特别做出强调,是因为实践中有些轻数据协议设计里面还会携带类似头像、验证码等的实体数据。
⑤ 长链接
长链接对于提升应用网络交互的及时性大有裨益,一方面用户使用时,节省了三次握手的时间等待,响应快捷;另一方面服务器具备了实时推送能力,不但可以及时提示用户重要信息,而且能通过推拉结合的异步方案,更好的提升用户体验。
长链接的维护包括链接管理、链接超时管理、任务队列管理等部分,设计实施复杂度相对高一些,尤其是在移动网络环境下。为了保持链路还需要做心跳机制(从另外一个角度看,这也是针对简单信息一个不错的PULL/PUSH时机,,但需注意数据传输要够轻,比如控制在0.5KB以内),而心跳机制是引入长链接方案复杂度的一个重要方面,移动网络链路环境复杂,国内网关五花八门,链路超时配置各有千秋,心跳时长选择学问比较大,不但要区分网络类型,还得区分不同运营商甚至不同省市,历史上曾经实践了2分钟的心跳间隔,最近比较多的产品实践选择4.5分钟的心跳间隔。而且长链接除了给移动网络尤其是空中信道带来负担外,移动设备自身的电量和流量也会有较大的消耗,同时还带来后端带宽和服务器投入增加。所以,除了一些粘性和活跃度很高、对信息到达实时性要求很高的通讯类APP外,建议谨慎使用长链接,或可以考虑采用下面的方式:
1) 退化长链接:即用户在前台使用时,保持一个长链接链路,活跃时通过用户使 用驱动网络IO保持链路可用;静默时通过设置HTTP Keep-Alive方式,亦或通过私有协议心跳方式来保持链路。一旦应用切换后台,且在5~10分钟内没有网络交互任务则自行关闭链路,这样在用户交互体验和资源消耗方面取得一个平衡点;
2) 定时拉取/询问:对于一些有PUSH需求的APP,我们可以采用一个云端可配置间隔时长的定时拉取/询问方案。有三个重点,一是定时的间隔云端可以配置,下发更新到客户端后下次生效;二是拉取/询问时,如果下发的指令有要求进一步PULL时,可以复用已建立的链路,即前述退化长链接的模式;三是定时拉取/询问时机在客户端要做时间上的均匀离散处理,避免大的并发查询带来带宽和负载的巨大毛刺;
3) 如果可能,优先使用OS内置的PUSH通道,比如iOS的APNS、Andriod的 GCM(Google这个以工程师文化著称的公司,在做OS级基础设施建设时,却表现出了很差的前瞻性和系统思考的能力,GCM的前身C2DM都没怎么普及使用就被替换了,这也意味着Android各种版本PUSH能力不 一致的问题。但无论怎么说,OS级的基础设施无论在性能、稳定性还是在效率上都会优于APP层自己实现的方案),实施推拉结合的方案。特别要提到的一点是,中国特色无所不在,国内运营商曾经封过APNS的PUSH端口2195,也会干扰GCM的端口5528,更别提这些底层服务的长链接会被运营商干扰。对于Android平台,还存在系统服务被各种定制修改的问题。别担心,办法总比问题多,保 持清醒;
⑥ 小心重试
自动重试是导致后台雪崩的重要因素之一。在移动网络不稳定的条件下,大量及时的重试不但不能达到预期,反而无谓的消耗移动设备的电量甚至流量。因此,我们在重试前要有一些差异化的考虑:
1) 当前移动设备的网络状况如何,如果没有网络,则不必重试;
2) 重试设定必要的时间间隔,因为移动接入网络抖动到恢复可能需要一点时间,马上重试并非最佳策略,反而可能无谓的消耗电量。实践中,可以在一次连接或IO失败(立即失败或超时)时,过3 ~ 5秒后再试;
3) 重试应设定必要的总时限,因为三个服务器列表比较长,每个服务器地址都要重试和等待若干次,最终可能导致接入调度FSM和服务器列表调度FSM流转耗时过长,此时用户侧体验表现为长时间等待无响应。总时限参数可以参考前述区分网络类型的超时管理中的任务超时值。一旦某次重试成功,重试总时限计时器要归零;
4) 服务器下发特定错误码(比如服务器故障、过载或高负载)时,提示客户端停止重试并告知安抚用户,我们在强监控这个主题下有详细的讨论;
每个目标服务器地址的重试次数、重试总时限和重试时间间隔最好能做到云端可配可控。
特别需要提出的一点是,移动APP采用HTTP短链接模式实现CS交互时,广泛的使用了系统原生组件或者开源组件,这些友好的模块把超时和重试都封装起来,其缺省值是否适合自己的业务特点,需要多多关注。使用前,最好能知其然更知其所以然。
⑦ 及时反馈
透明和尊重,会带来信任和默契,家庭如此、团队如此、用户亦如此。欲盖弥彰和装傻充愣也许短暂取巧,拉长时间轴来看,肯定要付出惨重的代价。及时和真诚的告知状况,赢得谅解和信任,小付出,大回报,试过都知道。
当发现因为网络不存在或者其它属于移动端设备链路的异常时,应该及时和显著的提示用户,让用户注意到当前有诸如网络不存在、FREE WIFI接入认证页面需确认等等问题,使用户可以及时处理或理解问题状态。
当发现是服务器问题时,应及时、显著和真诚的告知用户,争取用户的谅解。
网络异常提示或服务器故障通告等信息的呈现要做到一目了然,无二义和二次交互。
我们在强监控这个主题下有详细的方法讨论。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。