一、背景
如何度量和模拟“弱网络”对移动APP的开发有着重大的意义,比如:节约测试成本、易于问题重现、加快产品上线等。
一般的方法是使用“丢包率”和“网络延时”来定义和衡量“弱网络”。
二、手机接入服务器的流程
要讲这个问题,首先要来了解下手机接入服务器的流程。
- 首先,手机要通过无线网络协议,从基站获得无线链路分配,才能跟网络进行通讯。
- 无线网络基站、基站控制器这方面,会给手机进行信号的分配,已完成手机连接和交互。
- 获得无线链路后,会进行网络附着、加密、鉴权,核心网络会检查你是不是可以连接在这个网络上,是否开通套餐,是不是漫游等。核心网络有SGSN和GGSN,在这一步完成无线网络协议和有线以太网的协议转换。
- 再下一步,核心网络会给你进行APN选择、IP分配、启动计费。
- 再往下面,才是传统网络的步骤:DNS查询、响应,建立TCP链接,HTTP GET,RTTP RESPONSE 200 OK,HTTP RESPONSE DATA,LAST HTTP RESPONSE DATA,开始UI展现。
这是手机通过无线网络接入服务器的全过程。整个过程当中有几个困扰开发者的问题:
无线网络是怎么给手机分配到无线链路的?
核心网络有接入点(APN),这里的CMNET和CMWAP有什么区别,仅仅是协议不同吗吗?数据转发又有什么区别?一个数据包在不同网络上传输有不同吗?
用户怎么最快的找到正确的服务器?内容怎么快速有效的加载,在第一时间显示出来?
这几个问题的重点在于其中的几个连接点:
- 无线链路分配。这是一个物理实连接。
- IP层链接。这是一个逻辑虚连接。
- TCP层链接。这是一个逻辑虚连接。
- HTTP层链接。这是一个逻辑虚连接。
- 用户在线。这是一个逻辑虚连接。
3.2 一秒钟法则
根据以上情况,就形成无线网络的一大特点:秒级状态管理,秒级状态转换。这两个操作都在几百ms到几秒之间进行,对于维持连接来说时间太短,对于从无连接到有连接的转换来说时间又太长。
相比之下,有线网络的状态管理如ip分配、tcp连接释放,都是分钟级,而状态转换则是毫秒级。
这些通讯机制,同时加上无线网络的高延迟、高丢包。如何保证移动互联网的产品提供稳定的、可预期的服务质量,成为非常大的挑战:
2G网络上无线部分数据传输的延迟有几百ms,4G网络上无线部分传输延迟减少到几十ms,核心网状态转换、协议转换30~100ms,IP骨干网上的延迟又跟物理距离以及运营商互联互通质量有关,跨运营商50-400ms,同运营商5-80ms,这个还要取决于网络拥塞的情况。
无线网络误码率比有线高两个数量级,在不同时间段的波动也非常巨大。
怎么基于移动网络的特性去优化服务?
这就是我们总结的一秒钟法则:在一秒内要完成的规定动作。
- 2g网络:1秒内完成dns查询、和后台服务器建立连接
- 3g网络:1秒内完成首字显示(首字时间)
- wifi网络:1秒内完成首屏显示(首屏时间)
- 这些指标需要在终端度量,必须跟用户体验相关:首字时间、首屏时间都必须是用户可以直观感受到的。
四、优化思路
4.1 服务保证原则
从以上分析可知,如何保证移动互联网的产品提供稳定的、可预期的服务质量,具有非常大的挑战。以下几点原则可能会有帮助:
- 接口设计优化,接口的优化理论上不属于APP弱网络的优化,但是这个的API性能的问题,确实在网络条件不好的情况下才暴露无遗。大家都在谈论服务器的好坏,设备的性能高低,其实,对于一个良好的Server来说,绝大部分拖延请求速度的地方都是在IO上。包括,磁盘读写的IO,SQL查询的IO等等。常用的优化点:慢查询监控 、多次查询优化、常用接口cache等。
- 图片相关策略。
- 使用更快的图片格式,严格说也不算弱网下的优化,但一个更快的图片格式真的很重要!这里建议采用WebP格式。(WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。但WebP是一种有损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。)
- 不同网络的不同图片下发。如(对于原图是600X480的图片):2/3G使用低清晰度图片——>下发300X240,精度为80的图片、4G普通清晰度图片——>下发600X480,精度为80的图片、WiFi高清晰度图片(最好根据网速来判断,wifi也有慢的)——>下发600X480,精度为100的图片。
- 断线重连。这可能是最重的一个特性,因为在无线网络中有太多的原因导致数据连接中断了。这里可以使用CDN。(CDN 是构建在数据网络上的一种分布式的内容分发网。 CDN 的作用是采用流媒体服务器集群技术,克服单机系统输出带宽及并发能力不足的缺点,可极大提升系统支持的并发流数目,减少或避免单点失效带来的不良影响。)
- 由于创建连接是一个非常昂贵的操作,所以应尽量减少数据连接的创建次数,且在一次请求中应尽量以批量的方式执行任务。如果多次发送小数据包,应该尽量保证在2秒以内发送出去。在短时间内访问不同服务器时,尽可能地复用无线连接。
- 优化DNS查询。应尽量减少DNS查询、避免域名劫持、DNS污染,同时把用户调度到“最优接入点”。
- 减小数据包大小和优化包量。通过压缩、精简包头、消息合并等方式,来减小数据包大小和包量。
- 控制数据包大小不超过1500,避免分片。包括逻辑链路控制(Logic Link Control)分片、GGSN分片,以及IP分片。其中,当数据包大小超出GGSN所允许的最大大小时,GGSN的处理方式有以下三种:分片、丢弃和拒绝。
- 优化TCP socket参数,包括:是否关闭快速回收、初始RTO、初始拥塞窗口、socket缓存大小、Delay-ACK、Selective-ACK、TCP_CORK、拥塞算法(westwood/TLP/cubic)等。做这件事情的意义在于:由于2G/3G/4G/WIFI/公司内网等接入网络的QoS差异很大,所以不同网络下为了取得较好的服务质量,上述参数的取值差异可能会很大。
- 优化ACK包。在弱网络的情况下,TCP协议中的ACK包是非常昂贵的,延时甚至能够达到秒级别,而TCP协议的拥塞控制、快速重传、快速恢复等特性都非常依赖接收端反馈的ACK包。可想而知,如果发送端接收到的ACK包延时太长,会严重影响TCP协议的效率。但是如果发送ACK太多又会占用宝贵过多的无线资源。在移动网络下通信,“在可靠的连接上,如何在减少ACK包的情况下,降低数据包的延时”是研究的热点。基本的思想:平衡冗余包和ACK包个数,达到降低延时,提高吞吐量的目的。例如SGSN和GGSN之间的通信实现:二者之间通过UDP协议通信,发送者在无新的数据包的情况下,每隔一定的时间重试已发送的包,达到最大重试次数后,则丢弃该包。
- TCP的拥塞控制算法是以“丢包意味着网络出现拥塞”为假设设计的,很明显这个假设在无线网络环境下是不合适的。但是在无线网络环境下,在设计可靠UDP协议时是否能够完全丢弃拥塞控制呢?这里有其它的文章中提出了几种在无线网络环境下的TCP友好的拥塞控制算法,有兴趣可以自行查阅。
- 灵活使用长连接/短连接,支持不同协议(TCP/UDP, http、二进制协议等),支持不同端口等。
- 让用户觉得快。到这里已经不能算是技术层面的方法了,属于一种心理层面的博弈,一种改善用户体验的方式。比如:
- 不从0开始的进度条。不管网页的加载进度如何,不管网络条件如何,加载进度始终是从50%起,并且停留在大约98%进度左右的地方。
- 先显示文字在加载图片。同样是在Webview之中,图片或者多媒体的加载速度肯定是远远慢过文字的加载速度的。由于不同的webview显示和渲染效果不同,我们可以先让webview先显示文字,在显示图片。给用户一种可以先预览整个网页概况的感觉。
4.2 接入调度优化
接入调度优化首先要考虑的是减少DNS的影响。移动网络的DNS有如下特点:
- 骨干网无法识别移动用户在哪个城市,东西南北各个地方的调度没有充分调用。目前有一部分全国范围的DNS承载了超过40%的全网用户
- 很多山寨机的终端local dns设置是错误的
- 另外还有一些有线网络也一样会遇到的问题,如终端DNS解析滥用、域名劫持、DNS污染、老化、脆弱等。不过对于这些问题,桌面的自愈性会比较好,而在手机上则比较难以解决。
对于DNS的问题,有两条主要的解决思路:
- 减少DNS的请求、查询、更新,也就是做DNS缓存
- 在终端配置server list,直接访问IP,不用DNS
但仅仅这么做还不够,因为用户可能来自国内外不同的运营商,还需要进一步优化调度策略:
- DNS缓存需要多建立接入点,用不同域名区分
- IP列表需要更新以适应不同网络情况,要做到主动调度。好比最早我们只服务好移动用户就行,保证移动用户的接入质量优先,因为绝大多数用户集中在移动;现在国内有三个运营商,用户分布的比例在慢慢接近,要区分清楚;智能手机会用wifi,接入的是电信、联通还是哪个运营商,不知道,所以你不可能预先设置场景再if then,必须通过后台调度能力来解决。
再进一步优化,就产生一种融合的方式:
- 先做域名解析,客户端直接连接解析的IP,可以用http协议,也可以用tcp socket
- 多端口、多协议组合:不同协议有不同的限制,有些只能http,有些只能tcp socket,各种环境都要适应,客户端不能只支持一种协议
- 终端测速:接入点越来越多,接入哪个合适,要选择,可以通过终端测速来选择最快的。你当然可以每一次新建连接都做测速,但是这样建立连接时间可能会很长;我们可以给用户先建立连接后,在后台根据长期速度监控、当前测速的结果,来做动态调度。也就是说,第一次连接可能不是最优,连接建立后动态测速,再转移到最快接入点。更进一步就是建立网络profile,终端学习的思路。
关于测速采样的粒度,移动互联网取IP段是没用的,比较好的粒度是到网元级别,比如广东有20多个wap网关,每一个网关的情况都不一样,这就是一个比较合适的粒度。
最后强调一个所有的接入调度原则:不要把调度逻辑写死在客户端,一定要由后台完成。
4.3 协议优化
协议参数优化这块就简单列一下,是长期运营过程中总结的一些经验,在启动移动互联网服务时作为运营的规范,可以少走很多弯路:
- 关闭TCP快速回收
- Init RTO不低于3秒
- 初始拥塞控制窗口不小于10。因为大部分页面在10kB以下,很多请求在慢启动阶段已经结束,改为10可以降低小页面资源传输时延。内容越大,这个选项的效果就比较不明显。
- Socket buffer > 64k
- TCP滑动窗口可变
- 控制发包大小在1400字节以下,避免分片
协议优化的原则总结下来是这么几条:
- 连接重用
- 并发连接控制
- 超时控制
- 包头精简
- 内容压缩
- 选择更高效率的协议。无论是TCP、HTTP、UDP、长连接、GZIP、SPDY、WUP还是WebP,每一种协议、方案都有其道理,没有最优,只有是否适合你的产品和服务特点,需要大家在运营过程验证和取舍。
4.4 WAP接入点优化
关于WAP接入点优化,可能有些人会说,我们的App是高端大气上档次的应用,是不是就不用做WAP优化?实际上我们的统计显示,目前有5%-20%的用户选择的接入点是*WAP(CMWAP、3GWAP、CTWAP),这甚至包括一些iPhone终端。实际上,WAP网关本质是个代理,不完全是落后的东西,随着技术的进步也在演进,以后在组网架构中可能有综合网关、内容计费网关来取代目前的WAP网关,所以建议也要一并考虑。以下是做WAP优化需要注意的一些问题:
- 资费提醒页面
- 302跳转处理
- X-Online-Host使用与处理
- 包大小限制
- 劫持与缓存
- 正确获取资源包大小
4.5 业务逻辑优化
1、简化逻辑:交互繁琐的内容尽量用标识更新。举一个例子,我们在老版的手机QQ上做过一个测试:假如我有100个好友,用手机QQ完成登陆,完成好友列表更新一遍,需要3.5分钟。这肯定是不合理的。建议用信令状态来通知是否需要更新,同时合理利用缓存。在比如玩游戏,好友给你送了很多星星,是让用户一次一次点还是批量点?从优化的角度肯定是批量点,从用户体验的角度这也更加舒服。
另一方面,延长域名图标的缓存时间也可以有效地优化访问次数。我们把手机腾讯网图标的缓存时长从120分钟延长到2天后,访问次数优化了差不多35%。
2、柔性可用:这个意思就是在网络质量好的时候给高清大图,不好的时候先给用户看小图,点一下再拉取原图。举一个极端的例子,比如万一地震了,基站毁掉20%,用户要给家人报平安,这时候产品上就必须优化,比如只发送文字,合理降3,低网络消耗。另外在响应很慢的时候,需要给用户一些合理的页面提示,比如提示用户再过5秒会发送,所以你不要一直刷屏,这也可以减少访问对后台服务、对网络的冲击。
五 实战演示
5.1 一个最优调度设计示例
上面说了那么多,这里就给出一个实例帮助大家更直观的理解。
这里给出一个DNS系统设计来实现最优调度。其拓扑结构如下:
TGCP SDK的职责:
- 用HTTP的Get/Post方法从DNSvr获服务器和DNSvr本身的最优接入点列表。Get/Post方法的查询参数包括uin/openid、客户端版本号、IP列表的MD5(注意IP顺序)、域名列表、VIP、ServiceID等。
- 缓存访问服务器和DNSvr的IP列表,以及其它元数据(比如IP列表等),且以APN为主键。
- 满足一定的条件下,要主动更新缓存的IP列表,例如缓存过期。
Tconnd的职责:
- 路由查询请求给活动的DNSvr;
DNSvr的职责:
- 根据静态和动态策略来决定客户端的“最优接入点”。静态策略:根据uin/openid、客户端版本号或者强制规则来决定IP列表;动态策略:灯塔根据测速数据动态决定用户的服务器接入点。
- 支持以手动或自动的方式拉黑某些IP。自动方式:由服务器的接入tconnd向DNSvr上报其是否存活(需要向多个点上报,包括用公网IP上报),如果在一定时间内没有接收到上报或者上报消息中明确所有的逻辑服务器已经挂掉,则自动拉黑相应的IP。如果业务恢复,则自动激活相应的IP。如果项目组接入TGW,对于某个IP和端口是否可用,则需要考虑进程与VIP的映射关系。
- 在tcaplus中缓存灯塔的计算结果。此时要求DNSvr能够根据客户端IP判断所属的国家、省份、运营商和网关(可以通过访问MIG的IP库实现)。如果缓存了灯塔的计算结果,当缓存超时后,要重新从灯塔拉取相应数据。
灯塔的职责:
- 根据客户端IP和服务器接入点IP,返回最优的接入点列表,包括IP的排序,以及客户端接入的国家、省份、运营商、APN和网关。
Tcaplus的职责:
- 保存接入的IP列表和端口、静态策略,或缓存灯塔的计算结果;
主要的流程:
客户端批量解析域名流程
- TGCP以APN和域名列表为关键字查询缓存,如果存在且没有过期,则直接把IP返回给用户。如果指定强制解析域名列表,则跳过此步骤;
- TGCP用预配置或缓存的IP向DNSvr发起查询请求,如果成功返回结果,则执行步骤3,否则,重试IP列表中的其它IP,如果都失败,则用域名访问DNSvr。注意:如果是结果格式不正确,则使用上次的IP重试,不要更换IP重试。
- DNSvr比较客户端IP列表和当前最新的IP列表的MD5,如果相等,则告诉客户端不需要更新本地缓存。否则,TGCP把接入服务器和DNSvr的IP列表写入本地。注意:在访问服务器时,这些IP的优先级要高于静态配置在客户端的IP。
客户端使用域名访问服务器流程
- 如果本地存在有效的IP(即存在对应APN的IP列表,且没有失效),则使用IP访问服务器。
- 否则,发起“客户端批量解析域名流程”后,再访问服务器。
服务器接入tconnd主动上报状态流程:
- Tconnd周期性向DNSvr上报心跳消息,其中包含本接入点是否可用的信息。
- DNSvr在一定的时间内没有收到心跳消息或者相应的接入点不可用,则把相应的IP和端口拉黑,黑掉的IP不在下发给客户端。
注意:实际部署的时候,接入的Tconnd要向多个DNSvr接入tconnd上报。
向客户端主动push接入点列表的流程
- 当TGCP连接到服务器接入的Tconnd时,Tconnd要向DNSvr发起请求,以校验当前接入IP的质量和时效性。如果IP列表发生变化,Tconnd要把最新的IP列表下发给客户端缓存起来。
- 当TGCP下次访问服务器时,则使用最新的IP列表。
客户端访问DNSvr失败的流程
- 如果访问DNSvr失败(包括IP+域名),如果配置了本地IP,则直接用IP访问服务器,否则用域名访问。
优化传输层协议设计
在原有tconnd支持的可靠UDP的基础之上,添加以下逻辑:
- 数据压缩;
- 数据加密;
- 合并多个数据包;
- 支持流式数据传输,便于控制每个UDP包的大小,也便于数据加密和压缩;
- 可选地支持改进的拥塞控制算法;
- 即使没有接收到ACK包,也需要主动重试以发送的数据包;
5.2 Hybird开发下的一些优化
要处理在弱网络下的加载速度,那么我们要先确定一下我们的整个APP在哪个地方加载的速度如何,最长的加载路径在哪里,我们从而才有针对性的进行优化与修改。
5.2.1 WebView
如果是对是APP中内嵌的webview网页,针对网页体验优化已经由来已久了。我们可以使用Chrome的开发者模式,调整到Network模式下,将网络条件设置为3G去请求网页,那么我们就能够看出来一个网页加载的速度主要都耗费在哪个地方,如下图所示:
当然,html的加速方式有很多种
- 使用gulpgrunt进行打包压缩:jscss资源压缩,CSS Sprites合并等。
- 使用font-awesome替换图片:字体可以很好的兼容,无限放大,常用的图片都有