[TOC]
# 国内移动端网络所面临的各种DNS杂症
1)LocalDNS劫持;
2)平均访问延迟下降;
3)用户连接失败率下降。
<br>
**LocalDNS劫持:** 由于HttpDNS是通过ip直接请求http获取服务器A记录地址,不存在向本地运营商询问domain解析过程,所以从根本避免了劫持问题。 (对于http内容tcp/ip层劫持,可以使用验证因子或者数据加密等方式来保证传输数据的可信度)
**平均访问延迟下降:** 由于是ip直接访问省掉了一次domain解析过程,(即使系统有缓存速度也会稍快一些‘毫秒级’)通过智能算法排序后找到最快节点进行访问。
**用户连接失败率下降:** 通过算法降低以往失败率过高的服务器排序,通过时间近期访问过的数据提高服务器排序,通过历史访问成功记录提高服务器排序。如果ip(a)访问错误,在下一次返回ip(b)或者ip(c) 排序后的记录。
<br>
<br>
# 追根溯源,国内DNS问题的根源是什么?
我们得先得了解下现在国内各ISP运营商的LocalDNS的基本情况。
**国内运营商LocalDNS造成的这些问题,可以归为下以下3种原因:**
> 1)域名缓存;
> 2)解析转发;
> 3)LocalDNS递归出口NAT。
## 域名缓存
域名缓存很好理解,就是LocalDNS缓存了腾讯的域名的解析结果,不向腾讯权威DNS发起递归。
**示意图如下:**
<br>
![](https://pic4.zhimg.com/80/v2-049bd023631bc58f4cf89a927b385047_720w.jpg)
<br>
**为何LocalDNS要把域名解析结果进行缓存呢?原因有以下几个:**
1)保证用户访问流量在本网内消化:国内的各互联网接入运营商的带宽资源、网间结算费用、IDC机房分布、网内ICP资源分布等存在较大差异。为了保证网内用户的访问质量,同时减少跨网结算,运营商在网内搭建了内容缓存服务器,通过把域名强行指向内容缓存服务器的IP地址,就实现了把本地本网流量完全留在了本地的目的;
2)推送广告:有部分LocalDNS会把部分域名解析结果的所指向的内容缓存,并替换成第三方广告联盟的广告。
<br>
**以上类型的行为就是我们常说的域名缓存,域名缓存会导致用户产生以下的访问异常:**
A、仅对80端口的http服务做了缓存,如果域名是通过https协议或其它端口提供服务的,用户访问就会出现失败。比如支付服务、游戏通过指定端口连接connect server服务等;
B、缓存服务器的运维水平参差不齐,时有出现缓存服务器故障导致用户访问异常的问题。
## 6.2 解析转发
除了域名缓存以外,运营商的LocalDNS还存在解析转发的现象。解析转发是指运营商自身不进行域名递归解析,而是把域名解析请求转发到其它运营商的递归DNS上的行为。
**正常的LocalDNS递归解析过程是这样的:**
![](https://pic4.zhimg.com/80/v2-4f7c53a98b26143ffac8e53e94c940cb_720w.jpg)
<br>
<br>
**而部分小运营商为了节省资源,就直接将解析请求转发到了其它运营的递归LocalDNS上去了:**
![](https://pic2.zhimg.com/80/v2-af03e3e0f5d88a57790ebf120cdabcc9_720w.jpg)
这样的直接后果就是腾讯权威DNS收到的域名解析请求的来源IP就成了其它运营商的IP,最终导致用户流量被导向了错误的IDC,用户访问变慢。
<br>
<br>
## LocalDNS递归出口NAT
LocalDNS递归出口NAT指的是运营商的LocalDNS按照标准的DNS协议进行递归,但是因为在网络上存在多出口且配置了目标路由NAT,结果导致LocalDNS最终进行递归解析的时候的出口IP就有概率不为本网的IP地址。
<br>
**如下图所示:**
![](https://pic4.zhimg.com/80/v2-9fa91d6fba299036623a79861a18843f_720w.jpg)
这样的直接后果就是GSLB DNS收到的域名解析请求的来源IP还是成了其它运营商的IP,最终导致用户流量被导向了错误的IDC,用户访问变慢。
<br>
<br>
## 必须着手解决这些问题,但传统解决方案问题太多
运营商的LocalDNS解析域名异常,给对用户访问腾讯互联网业务的体验造成了非常大的损害。
**那么以前,我们是如何处理这些域名解析异常的问题的呢?**
**1)实时监控+商务推动:**
这种方案是目前腾讯的运营团队一直在使用的方案。这种方案就是周期比较长,毕竟通过行政手段来推动运营商来解决这个问题是比较耗时的。另外我们通过大数据分析,得出的结论是Top 3的问题用户均为移动互联网用户。对于这部分用户,我们有什么技术手段可以解决以上的问题呢?
**2)绕过自动分配DNS,使用114dns或Google public DNS:**
这个方案看上去很美好,114dns是国内最大的中立缓存DNS,而Google又是秉承不作恶理念的互联网工程帝国巨鳄,而且腾讯的权威DNS又支持edns-client-subnet功能,能直接识别使用Google publicDNS解析腾讯域名的用户的IP地址,不会出现流量调度失效。
但是问题来了:
> a. 如何在用户侧构造域名请求:对于PC端的客户端来说,构造一个标准的DNS请求包并不算什么难事。但在移动端要向一个指定的LocalDNS上发送标准的DNS请求包,而且要兼容各种iOS和android的版本的话,技术上是可行的,只是兼容的成本会很高;
> b. 推动用户修改配置极高:如果要推动用户手动修改PC的DNS配置的话,在PC端和手机客户端的WiFI下面还算勉强可行。但是要用户修改在移动互联网环境下的DNS配置,其难度不言而喻。
**3)完全抛弃域名,自建connectcenter进行流量调度:**
如果要采用这种这种方案的话,首先你就得要拿到一份准确的IP地址库来判断用户的归属,然后再制定个协议搭个connect center来做调度,然后再对接入层做调度改造。这种方案和2种方案一样,不是不能做,只是成本会比较高,尤其对于腾讯这种业务规模如此庞大的公司而言。
既然上面的这些传统方案都存在那么多的问题,那有没有一种调度精准、成本低廉、配置方便的基于域名的流量调度系统呢?答案是肯定的,我们在下一步继续分享。
<br>
<br>
# 当前主流的解决方案:HttpDNS出现了
## 8.1 什么HttpDNS?
HTTPDNS 利用 HTTP 协议与 DNS 服务器交互,代替了传统的基于 UDP 协议的 DNS 交互,绕开了运营商的 Local DNS,有效防止了域名劫持,提高域名解析效率。另外,由于 DNS 服务器端获取的是真实客户端 IP 而非 Local DNS 的 IP,能够精确定位客户端地理位置、运营商信息,从而有效改进调度精确性。
**HTTPDNS的原理如下图所示:**
![](https://pic4.zhimg.com/80/v2-3477f3945b392208e6de85f98ddb439f_720w.jpg)
## 8.2 HttpDns 主要解决的问题
**Local DNS 劫持:** 由于 HttpDns 是通过 IP 直接请求 HTTP 获取服务器 A 记录地址,不存在向本地运营商询问 domain 解析过程,所以从根本避免了劫持问题。
**平均访问延迟下降:** 由于是 IP 直接访问省掉了一次 domain 解析过程,通过智能算法排序后找到最快节点进行访问。
**用户连接失败率下降:** 通过算法降低以往失败率过高的服务器排序,通过时间近期访问过的数据提高服务器排序,通过历史访问成功记录提高服务器排序。
## 8.3 腾讯的HttpDNS思路
经过努力,腾讯公司的GSLB 团队推出的HttpDNS是为移动客户端量身定做的基于Http协议和域名解析的流量调度解决方案,专治LocalDNS解析异常以及流量调度不准。
详细介绍如下。
**腾讯的HttpDNS基本原理:**
![](https://pic4.zhimg.com/80/v2-d3e3c81e87773d1cf5122d926947f763_720w.jpg)
**HttpDNS的原理非常简单,主要有两步:**
A、客户端直接访问HttpDNS接口,获取业务在域名配置管理系统上配置的访问延迟最优的IP。(基于容灾考虑,还是保留次选使用运营商LocalDNS解析域名的方式);
B、客户端向获取到的IP后就向直接往此IP发送业务协议请求。以Http请求为例,通过在header中指定host字段,向HttpDNS返回的IP发送标准的Http请求即可。
<br>
<br>
**HttpDNS带来的优势:**
从原理上来讲,HttpDNS只是将域名解析的协议由DNS协议换成了Http协议,并不复杂。
**但是这一微小的转换,却带来了无数的收益:**
A、根治域名解析异常:由于绕过了运营商的LocalDNS,用户解析域名的请求通过Http协议直接透传到了腾讯的HttpDNS服务器IP上,用户在客户端的域名解析请求将不会遭受到域名解析异常的困扰;
B、调度精准:HttpDNS能直接获取到用户IP,通过结合腾讯自有专利技术生成的IP地址库以及测速系统,可以保证将用户引导的访问最快的IDC节点上;
C、实现成本低廉:接入HttpDNS的业务仅需要对客户端接入层做少量改造,无需用户手机进行root或越狱;而且由于Http协议请求构造非常简单,兼容各版本的移动操作系统更不成问题;另外HttpDNS的后端配置完全复用现有权威DNS配置,管理成本也非常低。总而言之,就是以最小的改造成本,解决了业务遭受域名解析异常的问题,并满足业务精确流量调度的需求;
D、扩展性强:HttpDNS提供可靠的域名解析服务,业务可将自有调度逻辑与HttpDNS返回结果结合,实现更精细化的流量调度。比如指定版本的客户端连接请求的IP地址,指定网络类型的用户连接指定的IP地址等。
<br>
<br>
**当然各位可能会问:** 用户将首选的域名解析方式切换到了HttpDNS,那么HttpDNS的高可用又是如何保证的呢?另外不同运营商的用户访问到同一个HttpDNS的服务IP,用户的访问延迟如何保证?
为了保证高可用及提升用户体验,HttpDNS通过接入了腾讯公网交换平台的BGP Anycast网络,与全国多个主流运营商建立了BGP互联,保证了这些运营商的用户能够快速地访问到HttpDNS服务;另外HttpDNS在多个数据中心进行了部署,任意一个节点发生故障时均能无缝切换到备份节点,保证用户解析正常。
**接入效果及未来展望:**
当前腾讯的HttpDNS方案已在腾讯内部接入了多个业务,覆盖数亿用户,并已持续稳定运行超过一年时间。而接入了HttpDNS的业务在用户访问体验方面都有了非常大的提升。
以某个接入HttpDNS的业务为例,该业务仅通过接入HttpDNS,在未做任何其它优化的情况下,用户平均访问延迟下降超过10%,访问失败率下降了超过五分之一,用户访问体验的效果提升非常显著。另外腾讯的HttpDNS服务除了在腾讯内部被广泛使用以外,也受到了业务同行的肯定。国内最大的publicDNS服务商114dns在受到腾讯DNS的启发下,也推出了HttpDNS服务。
在未来的日子里,腾讯GSLB团队将会在腾讯内部进一步推广HttpDNS服务,并将在实际业务的需求下对HttpDNS服务进行升级,如提供更为通用、安全、简单的接入协议,进一步提升接入用户的网络访问体验等等。希望HttpDNS能为各位在解决域名解析异常及全局流量调度失效方面提供一个简单、可行的思路。
<br>
<br>
# 作为创业团队,如何改造APP并支持HttpDNS?
作为创业团队,人力、财力、技术力量可能都无暇顾及这一块,但是移动端APP却实实在在面临文中提到的各种DNS问题,我们该怎么办呢?
## 9.1 使用第3方云服务商提供的HttpDNS接口
目前,国内有一部分厂商已经提供了这个解析服务,可以直接使用第三方服务。
目前,提供 HttpDns 解析服务的第3方服务商越来多,比如:[阿里云HttpDNS](https://link.zhihu.com/?target=https%3A//help.aliyun.com/product/30100.html)、[腾讯云HttpDNS](https://link.zhihu.com/?target=https%3A//cloud.tencent.com/product/hd)、[华为云HttpDNS](https://link.zhihu.com/?target=https%3A//bbs.huaweicloud.com/forum/thread-2076-1-1.html)等。
以阿里云的 HttpDNS为便,它的API 比较标准,直接发一个 Get 请求,带上请求参数,返回结果以 json 返回:
> [http://203.107.1.1/d?host=http://www.52im.net](https://link.zhihu.com/?target=http%3A//203.107.1.1/d%3Fhost%3Dhttp%3A//www.52im.net)
请求成功时,返回结果如下:
> {
> "host": "[http://www.linkedkeeper.com](https://link.zhihu.com/?target=http%3A//www.linkedkeeper.com)",
> "ips": \[
> "115.238.23.241",
> "115.238.23.251"
> \],
> "ttl": 57
> }
在移动端,将由 HttpDns 获得的 IP 地址在原有 URL 的基础上,将域名替换为 IP,然后用新的 URL 发起 HTTP 请求。
## 新浪微博团队分享的开源HttpDNS库:HTTPDNSLib
**HTTPDNSLib的Github地址:**[https://github.com/CNSRE/HTTPDNSLib](https://link.zhihu.com/?target=https%3A//github.com/CNSRE/HTTPDNSLib)
**HTTPDNSLib中实现的HttpDNS交互流程原理:**
![](https://pic4.zhimg.com/80/v2-3c6c30d6de214087ab1a012d3d0b8a8b_720w.jpg)
从上图中可以看出来 整个业务的交互流程,用户向查询模块传入一个URL地址,然后查询模块会检查缓存是否存在,不存在从httpdnsapi接口查询, 然后经过评估模块返回。在用户请求URL过程完毕时,需要将这次请求的结果反馈给 lib库的评估模块由评估模块入库记录本次质量数据。
**HttpDns Lib库交互详细流程:**
![](https://pic3.zhimg.com/80/v2-d62c297b18546f5a886fc612ab172086_720w.jpg)
上图这张详细流程图就更深入的说了下 lib的工作原理。有两条竖线讲图片分为了三个区域,分别是左部分、中间部分 和 右部分。
左部分是app主线程操作的事情,中间部分是app调用者线程中处理lib库事件逻辑的事情,右面部分是新线程独立处理事件的逻辑。
开始是里客户端调用方,传入一个 url,获取domain信息后由查询模块查询domain记录,查询模块会从内存缓存层查询,内存缓存层没有数据会,查询数据库,如果数据库也没有数据会请求本地 LocalDNS。从三个环节中任何一个环节拿到数据后, 都会进入下一个环节,如果没有拿到数据返回null结束。进入评估模块,根据五个插件进行排序, 排序后返回数据给客户端。
lib模块设定定时器,根据ttl过期时间来检查domain是否需要更新。 定时器是独立线程, 不会影响app主线程。 httpdns api请求数据, 先从自己配置的 httpdns api接口获取数据,如果获取不到会从 dnspod api接口获取如果也获取不到 直接从本地 localDNS获取数据,(从本地localDNS获取数据后期会改为发送UDP包封装dns协议从公共dns服务器直接获取,比如114dns等。dns服务器地址可自行设定。 )获取到数据后进入测速模块。 测速模块最新版本可以配置两种方式,一种是http空请求。 两个http头的交互,类似tcp首保耗时时间原理 ,用来测试链路最快。 另一种是ping命令,(icmp协议)来尽量最小化流量的消耗,考虑倒可能有的服务器禁ping就使用空http测速即可。 测速后将数据插入本地 cache 即可。
<br>
<br>
**下面是测试Demo的截图:**
![](https://pic1.zhimg.com/80/v2-d38cefea8fd16f3e6c33e20c9dd9b480_720w.jpg)
<br>
<br>
![](https://pic2.zhimg.com/80/v2-e87880fe196d59975be9be9f619c4ead_720w.jpg)
<br>
<br>
![](https://pic3.zhimg.com/80/v2-b6213eb6a99a885abdfa587661e04672_720w.jpg)
<br>
<br>
![](https://pic1.zhimg.com/80/v2-7af0da6a509ad5e359145c1ff3b0b8c4_720w.jpg)
<br>
<br>
有关HttpDns Lib库的详细介绍,请见:《[App域名劫持之DNS高可用 - 开源版HttpDNS方案详解](https://link.zhihu.com/?target=https%3A//mp.weixin.qq.com/s%3F__biz%3DMzAwMDU1MTE1OQ%3D%3D%26mid%3D209805123%26idx%3D1%26sn%3Dced8d67c3e2cc3ca38ef722949fa21f8%26scene%3D21%23wechat_redirect)》。
# 参考资料
[全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等](https://zhuanlan.zhihu.com/p/51529142)
- 第一部分 HTML
- meta
- meta标签
- HTML5
- 2.1 语义
- 2.2 通信
- 2.3 离线&存储
- 2.4 多媒体
- 2.5 3D,图像&效果
- 2.6 性能&集成
- 2.7 设备访问
- SEO
- Canvas
- 压缩图片
- 制作圆角矩形
- 全局属性
- 第二部分 CSS
- CSS原理
- 层叠上下文(stacking context)
- 外边距合并
- 块状格式化上下文(BFC)
- 盒模型
- important
- 样式继承
- 层叠
- 属性值处理流程
- 分辨率
- 视口
- CSS API
- grid(未完成)
- flex
- 选择器
- 3D
- Matrix
- AT规则
- line-height 和 vertical-align
- CSS技术
- 居中
- 响应式布局
- 兼容性
- 移动端适配方案
- CSS应用
- CSS Modules(未完成)
- 分层
- 面向对象CSS(未完成)
- 布局
- 三列布局
- 单列等宽,其他多列自适应均匀
- 多列等高
- 圣杯布局
- 双飞翼布局
- 瀑布流
- 1px问题
- 适配iPhoneX
- 横屏适配
- 图片模糊问题
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 内存空间
- 作用域
- 执行上下文栈
- 变量对象
- 作用域链
- this
- 类型转换
- 闭包(未完成)
- 原型、面向对象
- class和extend
- 继承
- new
- DOM
- Event Loop
- 垃圾回收机制
- 内存泄漏
- 数值存储
- 连等赋值
- 基本类型
- 堆栈溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍历对象属性
- 宽度、高度
- performance
- 位运算
- tostring( ) 与 valueOf( )方法
- JavaScript技术
- 错误
- 异常处理
- 存储
- Cookie与Session
- ES6(未完成)
- Babel转码
- let和const命令
- 变量的解构赋值
- 字符串的扩展
- 正则的扩展
- 数值的扩展
- 数组的扩展
- 函数的扩展
- 对象的扩展
- Symbol
- Set 和 Map 数据结构
- proxy
- Reflect
- module
- AJAX
- ES5
- 严格模式
- JSON
- 数组方法
- 对象方法
- 函数方法
- 服务端推送(未完成)
- JavaScript应用
- 复杂判断
- 3D 全景图
- 重载
- 上传(未完成)
- 上传方式
- 文件格式
- 渲染大量数据
- 图片裁剪
- 斐波那契数列
- 编码
- 数组去重
- 浅拷贝、深拷贝
- instanceof
- 模拟 new
- 防抖
- 节流
- 数组扁平化
- sleep函数
- 模拟bind
- 柯里化
- 零碎知识点
- 第四部分 进阶
- 计算机原理
- 数据结构(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 搜索算法
- 动态规划
- 二叉树
- 浏览器
- 浏览器结构
- 浏览器工作原理
- HTML解析
- CSS解析
- 渲染树构建
- 布局(Layout)
- 渲染
- 浏览器输入 URL 后发生了什么
- 跨域
- 缓存机制
- reflow(回流)和repaint(重绘)
- 渲染层合并
- 编译(未完成)
- Babel
- 设计模式(未完成)
- 函数式编程(未完成)
- 正则表达式(未完成)
- 性能
- 性能分析
- 性能指标
- 首屏加载
- 优化
- 浏览器层面
- HTTP层面
- 代码层面
- 构建层面
- 移动端首屏优化
- 服务器层面
- bigpipe
- 构建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack优化
- Webpack原理
- 实现loader
- 实现plugin
- tapable
- Webpack打包后代码
- rollup.js
- parcel
- 模块化
- ESM
- 安全
- XSS
- CSRF
- 点击劫持
- 中间人攻击
- 密码存储
- 测试(未完成)
- 单元测试
- E2E测试
- 框架测试
- 样式回归测试
- 异步测试
- 自动化测试
- PWA
- PWA官网
- web app manifest
- service worker
- app install banners
- 调试PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 饿了么整理
- 样式
- 技巧
- Vue音乐播放器
- Vue源码
- Virtual Dom
- computed原理
- 数组绑定原理
- 双向绑定
- nextTick
- keep-alive
- 导航守卫
- 组件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 动画(未完成)
- 异常监控、收集(未完成)
- 数据采集
- Sentry
- 贝塞尔曲线
- 视频
- 服务端渲染
- 服务端渲染的利与弊
- Vue SSR
- React SSR
- 客户端
- 离线包
- 第五部分 网络
- 五层协议
- TCP
- UDP
- HTTP
- 方法
- 首部
- 状态码
- 持久连接
- TLS
- content-type
- Redirect
- CSP
- 请求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服务端
- Linux
- Linux命令
- 权限
- XAMPP
- Node.js
- 安装
- Node模块化
- 设置环境变量
- Node的event loop
- 进程
- 全局对象
- 异步IO与事件驱动
- 文件系统
- Node错误处理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服务
- 负载均衡
- 获取用户IP
- 解决跨域
- 适配PC与移动环境
- 简单的访问限制
- 页面内容修改
- 图片处理
- 合并请求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自动化(未完成)
- docker
- 创建CLI
- 持续集成
- 持续交付
- 持续部署
- Jenkins
- 部署与发布
- 远程登录服务器
- 增强服务器安全等级
- 搭建 Nodejs 生产环境
- 配置 Nginx 实现反向代理
- 管理域名解析
- 配置 PM2 一键部署
- 发布上线
- 部署HTTPS
- Node 应用
- 爬虫(未完成)
- 例子
- 反爬虫
- 中间件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源码
- app.js
- config.js
- 消息队列
- RPC
- 性能优化
- 第七部分 总结
- Web服务器
- 目录结构
- 依赖
- 功能
- 代码片段
- 整理
- 知识清单、博客
- 项目、组件、库
- Node代码
- 面试必考
- 91算法
- 第八部分 工作代码总结
- 样式代码
- 框架代码
- 组件代码
- 功能代码
- 通用代码