网络优化是客户端几大技术方向中公认的一个深度领域,因此百度App给你们带来网络深度优化系列文章,其中包含系列《一》DNS优化,系列《二》链接优化,系列《三》弱网优化,但愿对你们在网络方向的学习和实践有所帮助。html
百度起家于搜索,整个公司的网络架构和部署都是基于标准的internet协议,目前已是全栈HTTPS,来到移动互联网时代后,总的基础架构不变,但在客户端上须要作不少优化工做。android
DNS(Domain Name System),它的做用是根据域名查出IP地址,它是HTTP协议的前提,只有将域名正确的解析成IP地址后,后面的HTTP流程才能进行,因此通常作网络优化会首选优化DNS。ios
DNS优化核心须要解决的问题有两点:git
【1】因为DNS劫持或故障形成的服务不可用,进而影响用户体验,影响公司的收入。github
【2】因为DNS调度不许确致使的性能退化,进而影响用户体验。算法
百度App承载着亿级流量,每一年都会遇到运营商DNS劫持或运营商DNS故障,总体影响很是很差,因此DNS优化刻不容缓,经过下图会更直观的了解运营商劫持或故障的原理。缓存
运营商劫持或故障的原理安全
既然咱们面临这么严峻的问题,那么咱们如何优化DNS呢?答案就是HTTPDNS。服务器
大部分标准DNS都是基于UDP与DNS服务器交互的,HTTPDNS则是利用HTTP协议与DNS服务器交互,绕开了运营商的Local DNS服务,有效防止了域名劫持,提升域名解析效率,下图是HTTPDNS的原理。微信
HTTPDNS原理
百度App HTTPDNS端上的实现是基于百度SYS团队的HTTPDNS服务,下图介绍了HTTPDNS的服务端部署结构。
HTTPDNS部署结构
HTTPDNS服务是基于BGP接入的,BGP英文Border Gateway Protocol,即边界网关协议,是一种在自治系统之间动态的交换路由信息的路由协议,BGP能够根据当前用户的运营商路由到百度服务点的对应集群上,对于第三方域名,服务点会经过百度部署在运营商的CDN节点向其余域名权威DNS发起查询,查询这个运营商下域名的最优IP。
百度App独立实现了端的HTTPDNS SDK,下图介绍了端HTTPDNS的总体架构。
端HTTPDNS的总体架构
DNS接口层解决的问题是屏蔽底层的细节,对外提供简单整洁的API,下降使用者的上手成本,提升开发效率。
DNS策略层经过多种策略的组合,使HTTPDNS服务在性能,稳定性,可用性上均保持较高的水准,下面讲解下每一个策略设计的初衷和具体实现。
这是一个很是关键的策略,主要解决HTTPDNS服务可用性的问题,实践证实,这个策略帮助百度App在异常状况下挽救回不少流量。
【1】当HTTPDNS服务不可用而且本地也没有缓存或者缓存失效的时候,会触发降级策略,降级成运营商的localDNS方案,虽然存在运营商事故或者劫持的风险,但保障了DNS服务的可用性。
【2】当HTTPDNS服务和localDNS服务双双不可用的状况下,会触发backup策略,使用端上的backup IP。
什么是backup IP?backup IP是多组根据域名分类的IP列表,可云端动态更新,方便后续运维同窗调整服务端的节点IP,不是全部域名都有对应的backup IP列表,目前百度App只能保证核心域名的可用性。
既然是一组IP,便有选取问题,backup IP选取机制是怎样的呢?咱们的中心思想就是要在端上利用最小的代价,而且考虑服务端的负载均衡,获得相对正确或者合理的选取结果。经过运营商和地理信息,能够选择一个相对较优的IP,但获取地理信息须要很大耗时,外加频次很高,代价很大,因此咱们选择了RR算法来代替上面的方法(RR算法是Round-Robin,轮询调度),这样客户端的代价下降到最小,服务端也实现了负载均衡。
【1】HTTPDNS解决的核心问题就是安全,标准的DNS查询大部分是基于UDP的,但也有基于TCP的,若是UDP被封禁,就须要使用TCP。无论是UDP仍是TCP,安全性都是没有保障的,HTTPDNS查询是基于标准的HTTP协议,为了保证安全咱们会在HTTP上加一层TLS(安全传输层协议),这即是HTTPS。
【2】解决了传输层协议的安全性后,咱们要解决下域名解析的问题,上面咱们提到HTTPDNS服务是基于BGP接入的,在端上采用VIP方式请求HTTPDNS数据(VIP即Virtual IP,VIP并无与某设备存在一定的绑定关系,会跟随主备切换之类的状况发生而变换,VIP提供的服务是对应到某一台或若干台服务器的),既然请求原始数据须要使用IP直连的方式,那么就摆脱了运营商localDNS的解析限制,这样即便运营商出现了故障或者被劫持,都不会影响百度App的可用性。
HTTPDNS服务提供了两类HTTP接口,用于请求最优域名结果。第一种是多域名接口,针对不一样的产品线,下发产品线配置的域名,第二种是单域名接口,只返回你要查询的那个域名结果,这样的设计和标准的DNS查询基本是同样的,只不过是从UDP协议变成了HTTP协议。
【1】多域名接口会在App冷启动和网络切换的时候请求一次,目的是在App的网络环境初始化或者变化的时候预先获取域名结果,这样也会减小单域名接口的请求次数。
【2】单域名接口会在本地cache过时后,由用户的操做触发网络请求,进而作一次单域名请求,用户此次操做的DNS结果会降级成localDNS的结果,但在没有过时的状况下,下次会返回HTTPDNS的结果。
IP选取策略解决的核心问题是最优IP的选取,避免由于接入点的选取错误形成的跨运营商耗时。HTTPDNS服务会将最优IP按照顺序下发,客户端默认选取第一个,这里没有作客户端的连通性校验的缘由,主要仍是担忧端上的性能问题,不过有容灾策略兜底,综合评估仍是能够接受的。
你们对于DNS缓存并不陌生,它主要是为了提高访问效率,操做系统,网络库等都会作DNS缓存。
DNS缓存中一个重要的概念就是TTL(Time-To-Live),在localDNS中针对不一样的域名,TTL的时间是不同的,在HTTPDNS中这个值由服务端动态下发,百度App目前全部的域名TTL的配置是5分钟,过时后若是没有新的IP将继续沿用老的IP,固然也能够选择不沿用老的IP,而降级成localDNS的IP,那么这就取决于localDNS对于过时IP的处理。
若是HTTPDNS的命中率是100%,在保证HTTPDNS服务稳定高效的前提下,咱们就能够作到防劫持,提高精准调度的能力。
【1】为了提高HTTPDNS的命中率,咱们选择使用多域名接口,在冷启动和网络切换的时候,批量拉取域名结果并缓存在本地,便于接下来的请求使用。
【2】为了再一次提高HTTPDNS的命中率,当用户操做触发网络请求,获取域名对应的IP时,会提早进行本地过时时间判断,时间是60s,若是过时,会发起单域名的请求并缓存起来,这样会持续延长域名结果的过时时间。本地过时时间与上面提到的TTL是客户端和服务端的双重过时时间,目的是在异常状况下能够双重保证过时时间的准确性。
基础能力层主要提供给DNS策略层所须要的基础能力,包括IPv4/IPv6协议栈探测的能力,数据传输的能力,缓存实现的能力,下面将讲解每种能力的具体实现
百度App的IPv6改造正在如火如荼的进行中,端上在HTTPDNS的IP选取上如何知道目前属于哪一个协议栈成为关键性问题,而且这种判断要求性能极高,由于IP选取的频次实在是过高了。
咱们选取的方案是UDP Connect,那么何为UDP Connect?你们都知道TCP是面向链接的,传输数据前客户端都要调用connect方法经过三次握手创建链接,UDP是面向无链接的,无需创建链接便能收发数据,可是若是咱们调用了UDP的connect方法会发生什么呢?当咱们调用UDP的connect方法时,系统会检测其端口是否可用,地址是否正确,而后记录对端的IP地址和端口号,返回给调用者,因此UDP Connect不会像TCP Connect发起三次握手,发生网络真实损耗,UDP客户端只有调用send或者sendto方法后才会真正发起真实网络损耗。
UDP Connect原理
有了UDP Connect的基础保障,咱们在上层作了缓存机制,用来减小系统调用的损耗,时机上目前仅在冷启动和网络切换会触发探测,在同一种网络制式下探测一次基本能够确保当前网络是IPv4栈仍是IPv6栈。
目前百度App客户端对于IPv4/IPv6双栈的策略是保守的,仅在IPv6-only的状况下使用v6的IP,其他使用的都是v4的IP,双栈下的方案后续须要优化,业内目前标准的作法是happy eyeball算法,什么叫happy eyeball呢?就是不会由于IPv4或IPv6的故障问题,致使用户的眼球一直在等待加载或者出错,这就是happy eyeball名字的由来。happy eyeball有v1版本RFC6555和v2版本RFC8305,前者是Cisco提出来的,后者是苹果提出来的。happy eyeball解决的核心问题是,复杂环境下v4和v6 IP选取的问题,它是一套总体解决方案,对于域名查询的处理,地址的排序,链接的尝试等方面均作出了规定,感兴趣的同窗能够查看参考资料里的【5】和【6】。
数据传输主要提供网络请求的能力和数据解析的能力。
【1】网络请求失败重试的机制,获取HTTPDNS结果的成功率会大大影响HTTPDNS的命中率,因此客户端会有一个三次重试的机制,保障成功率。
【2】数据解析异常的机制,若是获取的HTTPDNS的结果存在异常,将不会覆盖端上的缓存。
缓存的实现基本能够分为磁盘缓存和内存缓存,对于HTTPDNS的缓存场景,咱们是选其一仍是都选择呢?百度App选择的是内存缓存,目的是防止咱们本身的服务出现问题,运维同窗在紧急状况下切换流量,若是作了磁盘缓存,会致使百度App在重启后也可能不可用,但这种问题会致使APP在冷启动期间,HTTPDNS结果未返回前,仍是存在故障或者劫持的风险,综合评估来看能够接受,若是出现这种极端状况,影响的是冷启动阶段的一些请求,但只要HTTPDNS结果返回后便会恢复正常。
百度App目前客户端网络架构因为历史缘由还未统一,不过咱们正朝着这个目标努力,下面着重介绍下HTTPDNS在Android和iOS网络架构中的位置及实践。
百度App的Android网络流量都在okhttp之上,上层进行了网络门面的封装,封装内部的实现细节和对外友好的API,供各个业务和基础模块使用,在okhttp上咱们扩展了DNS模块,使用HTTPDNS替换了原有的系统DNS。
HTTPDNS在Android网络架构的位置
百度App的iOS网络流量都在cronet(chromium的net模块)之上,上层咱们使用AOP的方式将cronet stack注入进URLSession里,这样咱们就能够直接使用URLSession的API进行网络的操做并且更易于系统维护,在上层封装了网络门面,供各个业务和基础模块使用,在cronet内部咱们修改了DNS模块,除了原有的系统DNS逻辑外,还添加了HTTPDNS的逻辑。iOS上还有一部分流量是在原生URLSession上,主要是有些第三方业务没有使用cronet但还想单独使用HTTPDNS的能力,因此就有了下面的HTTPDNS封装层,方法是在上层直接将域名替换成IP,域名对于底层不少机制是相当重要的,好比https校验,cookie,重定向,SNI(Server Name Indication)等,因此将域名修改为了IP直连后,咱们又处理了以上三种状况,保证请求的可用性。
HTTPDNS在iOS网络架构的位置
DNS优化的收益主要有两点,一是防止DNS的劫持(在出问题时显得尤其重要),下降网络时延(在调度不许确的状况下,会增大网络的时延,下降用户的体验),这两点收益须要结合业务来讲,以百度App Feed业务为例,第一点上咱们取得了比较大的效果,iOS劫持率由0.12%下降到0.0002%,Android劫持率由0.25%下降到0.05%,第二点的收益不明显,缘由在于Feed业务主要目标群体在国内,百度在国内节点布局相对丰富,服务总体质量也较高,即便出现调度不许确的状况,差值也不会太大,但若是在国外状况可能会差不少。
DNS优化是个持续性的话题,上面介绍的百度App的一些经验和作法并不见得完美,但咱们会持续深刻的优化下去,为百度App的DNS能力保驾护航。最后感谢你们的辛苦阅读,但愿对你有所帮助,后面会继续推出-百度App网络深度优化系列《二》链接优化,敬请期待。
作为一个工程师,如何才能作好网络优化这件事情,是个值得咱们交流探讨的话题,我的认为应该从如下五方面入手。
【1】基础知识要了解学习,要夯实,网络相关的内容不少,很杂,不易学习,啃过IETF发布的RFC的同窗应该深有感触。
【2】学会将看不见的网络变成看得见的,不少自认为对于网络很了解的同窗,动不动就背诵tcp协议原理,拥塞控制算法,滑动窗口大小等,但真正遇到线上问题,无从下手。对于客户端同窗,咱们在PC上要学会使用tcpdump和Wireshark等工具,适当使用Fiddler和Charles等工具,不少时候电脑和手机的网络环境不见得一致,因此要在手机上使用iNetTools,Ping&DNS或终端工具。学会使用工具后,要学着创造不一样的网络环境,有不少工具能帮助你完成这点,好比苹果的Network Link Conditioner,FaceBook的ATC(Augmented Traffic Control)等。具有以上两个场景后,你的第一条储备就发挥了做用,你要能看懂握手过程,传输过程,异常断开过程等。
【3】有了以上两点的准备,接下来须要一个会出现各类网络问题的平台,给你积累经验,让一个个高压下的线上问题锤炼你,折磨你。
【4】网络优化是须要数据支撑的,但数据的采集和分析是须要经验的,有些数据一眼看下去就是不靠谱的,有些数据怎么分析都是负向收益的,通常来讲是有三重奏来对数据进行分析的,一,线下数据的采集和分析,得出正向收益,二,灰度数据的采集和分析,得出正向收益,三,线上数据的采集和分析,得出正向收益。
【5】数据的正向收益,不能彻底证实提高了用户的体验,因此不少时候须要针对特定场景,特定case来分析和优化,就算是你们公认作的很好的微信,也不是在全部场景下都能保证体验上的最佳。
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/android_build_instructions.md
https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ios/build_instructions.md
https://github.com/Tencent/mars
https://tools.ietf.org/html/rfc7858
https://tools.ietf.org/html/rfc6555
https://tools.ietf.org/html/rfc8305