百度 App 网络深度优化系列《一》DNS 优化

阅读数:108 2019 年 11 月 28 日 08:00

百度App网络深度优化系列《一》DNS优化

一、前言

网络优化是客户端几大技术方向中公认的一个深度领域,所以百度 App 给大家带来网络深度优化系列文章,其中包含系列《一》DNS 优化,系列《二》连接优化,系列《三》弱网优化,希望对大家在网络方向的学习和实践有所帮助。
百度起家于搜索,整个公司的网络架构和部署都是基于标准的 internet 协议,目前已经是全栈 HTTPS,来到移动互联网时代后,总的基础架构不变,但在客户端上需要做很多优化工作。
DNS(Domain Name System),它的作用是根据域名查出 IP 地址,它是 HTTP 协议的前提,只有将域名正确的解析成 IP 地址后,后面的 HTTP 流程才能进行,所以一般做网络优化会首选优化 DNS。

二、背景

DNS 优化核心需要解决的问题有两点:
【1】由于 DNS 劫持或故障造成的服务不可用,进而影响用户体验,影响公司的收入。
【2】由于 DNS 调度不准确导致的性能退化,进而影响用户体验。
百度 App 承载着亿级流量,每年都会遇到运营商 DNS 劫持或运营商 DNS 故障,整体影响非常不好,所以 DNS 优化刻不容缓,通过下图会更直观的了解运营商劫持或故障的原理。
百度App网络深度优化系列《一》DNS优化

运营商劫持或故障的原理
## 三、HTTPDNS 既然我们面临这么严峻的问题,那么我们如何优化 DNS 呢?答案就是 HTTPDNS。 大部分标准 DNS 都是基于 UDP 与 DNS 服务器交互的,HTTPDNS 则是利用 HTTP 协议与 DNS 服务器交互,绕开了运营商的 Local DNS 服务,有效防止了域名劫持,提高域名解析效率,下图是 HTTPDNS 的原理。

百度App网络深度优化系列《一》DNS优化

HTTPDNS 原理
百度 App HTTPDNS 端上的实现是基于百度 SYS 团队的 HTTPDNS 服务,下图介绍了 HTTPDNS 的服务端部署结构。 !
HTTPDNS 部署结构
HTTPDNS 服务是基于 BGP 接入的,BGP 英文 Border Gateway Protocol,即边界网关协议,是一种在自治系统之间动态的交换路由信息的路由协议,BGP 可以根据当前用户的运营商路由到百度服务点的对应集群上,对于第三方域名,服务点会通过百度部署在运营商的 CDN 节点向其他域名权威 DNS 发起查询,查询这个运营商下域名的最优 IP。 百度 App 独立实现了端的 HTTPDNS SDK,下图介绍了端 HTTPDNS 的整体架构。

百度App网络深度优化系列《一》DNS优化

端 HTTPDNS 的整体架构
### DNS 接口层: DNS 接口层解决的问题是屏蔽底层的细节,对外提供简单整洁的 API,降低使用者的上手成本,提高开发效率。 ### DNS 策略层: DNS 策略层通过多种策略的组合,使 HTTPDNS 服务在性能,稳定性,可用性上均保持较高的水准,下面讲解下每个策略设计的初衷和具体实现。 #### 1. 容灾策略 这是一个非常关键的策略,主要解决 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,轮询调度),这样客户端的代价降低到最小,服务端也实现了负载均衡。 #### 2. 安全策略 【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 的可用性。 #### 3. 任务调度策略 HTTPDNS 服务提供了两类 HTTP 接口,用于请求最优域名结果。第一种是多域名接口,针对不同的产品线,下发产品线配置的域名,第二种是单域名接口,只返回你要查询的那个域名结果,这样的设计和标准的 DNS 查询基本是一样的,只不过是从 UDP 协议变成了 HTTP 协议。 【1】多域名接口会在 App 冷启动和网络切换的时候请求一次,目的是在 App 的网络环境初始化或者变化的时候预先获取域名结果,这样也会减少单域名接口的请求次数。 【2】单域名接口会在本地 cache 过期后,由用户的操作触发网络请求,进而做一次单域名请求,用户这次操作的 DNS 结果会降级成 localDNS 的结果,但在没有过期的情况下,下次会返回 HTTPDNS 的结果。 #### 4.IP 选取策略 IP 选取策略解决的核心问题是最优 IP 的选取,避免因为接入点的选取错误造成的跨运营商耗时。HTTPDNS 服务会将最优 IP 按照顺序下发,客户端默认选取第一个,这里没有做客户端的连通性校验的原因,主要还是担心端上的性能问题,不过有容灾策略兜底,综合评估还是可以接受的。 #### 5. 缓存策略 大家对于 DNS 缓存并不陌生,它主要是为了提升访问效率,操作系统,网络库等都会做 DNS 缓存。 DNS 缓存中一个重要的概念就是 TTL(Time-To-Live),在 localDNS 中针对不同的域名,TTL 的时间是不一样的,在 HTTPDNS 中这个值由服务端动态下发,百度 App 目前所有的域名 TTL 的配置是 5 分钟,过期后如果没有新的 IP 将继续沿用老的 IP,当然也可以选择不沿用老的 IP,而降级成 localDNS 的 IP,那么这就取决于 localDNS 对于过期 IP 的处理。 #### 6. 命中率策略 如果 HTTPDNS 的命中率是 100%,在保证 HTTPDNS 服务稳定高效的前提下,我们就可以做到防劫持,提升精准调度的能力。 【1】为了提升 HTTPDNS 的命中率,我们选择使用多域名接口,在冷启动和网络切换的时候,批量拉取域名结果并缓存在本地,便于接下来的请求使用。 【2】为了再一次提升 HTTPDNS 的命中率,当用户操作触发网络请求,获取域名对应的 IP 时,会提前进行本地过期时间判断,时间是 60s,如果过期,会发起单域名的请求并缓存起来,这样会持续延长域名结果的过期时间。本地过期时间与上面提到的 TTL 是客户端和服务端的双重过期时间,目的是在异常情况下可以双重保证过期时间的准确性。 ### 基础能力层: 基础能力层主要提供给 DNS 策略层所需要的基础能力,包括 IPv4/IPv6 协议栈探测的能力,数据传输的能力,缓存实现的能力,下面将讲解每种能力的具体实现 #### 1.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 方法后才会真正发起真实网络损耗。

百度App网络深度优化系列《一》DNS优化

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】。 ### 2. 数据传输: 数据传输主要提供网络请求的能力和数据解析的能力。 【1】网络请求失败重试的机制,获取 HTTPDNS 结果的成功率会大大影响 HTTPDNS 的命中率,所以客户端会有一个三次重试的机制,保障成功率。 【2】数据解析异常的机制,如果获取的 HTTPDNS 的结果存在异常,将不会覆盖端上的缓存。 ### 3. 缓存实现: 缓存的实现基本可以分为磁盘缓存和内存缓存,对于 HTTPDNS 的缓存场景,我们是选其一还是都选择呢?百度 App 选择的是内存缓存,目的是防止我们自己的服务出现问题,运维同学在紧急情况下切换流量,如果做了磁盘缓存,会导致百度 App 在重启后也可能不可用,但这种问题会导致 APP 在冷启动期间,HTTPDNS 结果未返回前,还是存在故障或者劫持的风险,综合评估来看可以接受,如果出现这种极端情况,影响的是冷启动阶段的一些请求,但只要 HTTPDNS 结果返回后便会恢复正常。 ## 四、HTTPDNS 的最佳实践 百度 App 目前客户端网络架构由于历史原因还未统一,不过我们正朝着这个目标努力,下面着重介绍下 HTTPDNS 在 Android 和 iOS 网络架构中的位置及实践。 HTTPDNS 在 Android 网络架构的位置及实践 百度 App 的 Android 网络流量都在 okhttp 之上,上层进行了网络门面的封装,封装内部的实现细节和对外友好的 API,供各个业务和基础模块使用,在 okhttp 上我们扩展了 DNS 模块,使用 HTTPDNS 替换了原有的系统 DNS。 !
HTTPDNS 在 Android 网络架构的位置
HTTPDNS 在 iOS 网络架构的位置及实践 百度 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 直连后,我们又处理了以上三种情况,保证请求的可用性。

百度App网络深度优化系列《一》DNS优化

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 ** 本文转载自百度 App 技术公众号。** ** 原文链接:**https://mp.weixin.qq.com/s/iaPtSF-twWz-AN66UJUBDg

评论

发布