问题描述:某些地区性客户反应应用更新无法下载问题
排查原因:用户使用自带浏览器打开下载地址(无法下载)(浏览器打不开说明不是应用问题)
协同运维同学和客户沟通测试 结果 再用户所在地我们域名DNS被劫持导致域名DNS解析时找不到对应对的ip
临时方案: 先发一个新域名的下载地址给客户 因为域名比较新所以没有被劫持 可以下载
app端:添加容错 在使用旧域名无法下载的时候,使用新域名下载
这样就app的下载功能就可以正常使用,但是这只是一个临时解决方案,要是新旧域名都被劫持了怎么办了
所以我们还是需要找最终解决方案
在解决之前 我们首先要了解下什么是DNS
1.1什么是DNS
DNS(Domain Name System,域名系统),DNS 服务用于在网络请求时,将域名转为 IP 地址。能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。
DNS的基一原理如下图所示:
1.2 DNS 域名系统结构
如上图所示,典型DNS域名系统的结构如下:
1)Root 域名:DNS 域名使用时,规定由尾部句号来指定名称位于根或更高级别的域层次结构;
2)Top Level 顶级域名:用来指示某个国家、地区或组织使用的名称的类型名称。如 .net;
3)Second Level 域名:个人或组织在 Internet 上使用的注册名称。如 52im.net;
4)Third Level 域名:已注册的二级域名派生的域名。如 docs.52im.net。
1.3 DNS 解析过程
如上图所示,这是一个典型的域名解析过程:
1)浏览器中输入 www.baidu.com,发出解析请求;
2)本机的域名解析器 resolver 程序查询本地缓存和 host 文件中是否为域名的映射关系,如果有则调用这个 IP 地址映射,完成解析;
3)如果 hosts 与本地解析器缓存都没有相应的网址映射关系,则本地解析器会向 TCP/IP 参数中设置的首选 DNS 服务器(我们叫它 Local DNS 服务器)发起一个递归的查询请求;
4)服务器收到查询时,如果要查询的域名由本机负责解析,则返回解析结果给客户机,完成域名解析,此解析具有权威性。如果要查询的域名,不由 Local DNS 服务器解析,但该服务器已缓存了此网址映射关系,则调用这个 IP 地址映射,完成域名解析,此解析不具有权威性;
5)如果 Local DNS 服务器本地区域文件与缓存解析都失效,则根据 Local DNS 服务器的设置(是否递归)进行查询,如果未用开启模式,Local DNS 就把请求发至13台 Root DNS。如果用的是递归模式,此 DNS 服务器就会把请求转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根 DNS 或把转请求转至上上级,以此循环;
6)Root DNS 服务器收到请求后会判断这个域名是谁来授权管理,并会返回一个负责该顶级域名服务器的一个 IP;
7)Local DNS 服务器收到 IP 信息后,将会联系负责 .net 域的这台服务器;
8)负责 .com 域的服务器收到请求后,如果自己无法解析,它就会找一个管理 .net 域的下一级 DNS 服务器地址给本地 DNS 服务器;
9)当 Local DNS 服务器收到这个地址后,就会找 百度 域服务器,10、11重复上面的动作,进行查询;
10)最后 www.baidu.com 返回需要解析的域名的 IP 地址给 Local DNS 服务器;
11)Local DNS 服务器缓存这个解析结果(同时也会缓存,6、8、10返回的结果);
12)Local DNS 服务器同时将结果返回给本机域名解析器;
13)本机缓存解析结果;
14)本机解析器将结果返回给浏览器;
15)浏览器通过返回的 IP 地址发起请求。
1.4国内移动端网络所面临的各种DNS杂症
总结下来,DNS的这些咋整主要的带来了三类问题:
1)LocalDNS劫持;
2)平均访问延迟下降;
3)用户连接失败率下降。
LocalDNS劫持: 由于HttpDNS是通过ip直接请求http获取服务器A记录地址,不存在向本地运营商询问domain解析过程,所以从根本避免了劫持问题。 (对于http内容tcp/ip层劫持,可以使用验证因子或者数据加密等方式来保证传输数据的可信度)
平均访问延迟下降: 由于是ip直接访问省掉了一次domain解析过程,(即使系统有缓存速度也会稍快一些‘毫秒级’)通过智能算法排序后找到最快节点进行访问。
用户连接失败率下降: 通过算法降低以往失败率过高的服务器排序,通过时间近期访问过的数据提高服务器排序,通过历史访问成功记录提高服务器排序。如果ip(a)访问错误,在下一次返回ip(b)或者ip(c) 排序后的记录。
2问题根源
要解决问题,我们得先得了解下现在国内各ISP的LocalDNS的基本情况。国内运营商LocalDNS造成的用户访问异常可以归为下三类:
2.1 域名缓存
域名缓存很好理解,就是LocalDNS缓存了腾讯的域名的解析结果,不向腾讯权威DNS发起递归。
为何LocalDNS要把域名解析结果进行缓存呢?原因有以下几个:
(1)保证用户访问流量在本网内消化:国内的各互联网接入运营商的带宽资源、网间结算费用、IDC机房分布、网内ICP资源分布等存在较大差异。为了保证网内用户的访问质量,同时减少跨网结算,运营商在网内搭建了内容缓存服务器,通过把域名强行指向内容缓存服务器的IP地址,就实现了把本地本网流量完全留在了本地的目的。
(2)推送广告:有部分LocalDNS会把部分域名解析结果的所指向的内容缓存,并替换成第三方广告联盟的广告。
这种类型的行为就是我们常说的域名缓存,域名缓存会导致用户产生以下的访问异常:
A、仅对80端口的http服务做了缓存,如果域名是通过https协议或其它端口提供服务的,用户访问就会出现失败。比如支付服务、游戏通过指定端口连接connect server服务等。
B、缓存服务器的运维水平参差不齐,时有出现缓存服务器故障导致用户访问异常的问题。
2.2、解析转发:
除了域名缓存以外,运营商的LocalDNS还存在解析转发的现象。解析转发是指运营商自身不进行域名递归解析,而是把域名解析请求转发到其它运营商的递归DNS上的行为。
正常的LocalDNS递归解析过程是这样的
而部分小运营商为了节省资源,就直接将解析请求转发到了其它运营的递归LocalDNS上去了:
这样的直接后果就是腾讯权威DNS收到的域名解析请求的来源IP就成了其它运营商的IP,最终导致用户流量被导向了错误的IDC,用户访问变慢。
2.3 LocalDNS递归出口NAT
LocalDNS递归出口NAT指的是运营商的LocalDNS按照标准的DNS协议进行递归,但是因为在网络上存在多出口且配置了目标路由NAT,结果导致LocalDNS最终进行递归解析的时候的出口IP就有概率不为本网的IP地址。
如下图所示:
这样的直接后果就是GSLB DNS收到的域名解析请求的来源IP还是成了其它运营商的IP,最终导致用户流量被导向了错误的IDC,用户访问变慢。
3、必须着手解决这些问题,但传统解决方案问题太多
运营商的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种方案一样,不是不能做,只是成本会比较高,尤其对于腾讯这种业务规模如此庞大的公司而言。
既然上面的这些传统方案都存在那么多的问题,那有没有一种调度精准、成本低廉、配置方便的基于域名的流量调度系统呢?
4、当前主流的解决方案:HttpDNS出现了
4.1 什么HttpDNS?
HTTPDNS 利用 HTTP 协议与 DNS 服务器交互,代替了传统的基于 UDP 协议的 DNS 交互,绕开了运营商的 Local DNS,有效防止了域名劫持,提高域名解析效率。另外,由于 DNS 服务器端获取的是真实客户端 IP 而非 Local DNS 的 IP,能够精确定位客户端地理位置、运营商信息,从而有效改进调度精确性。
HTTPDNS的原理如下图所示:
4.2 HttpDns 主要解决的问题
Local DNS 劫持:由于 HttpDns 是通过 IP 直接请求 HTTP 获取服务器 A 记录地址,不存在向本地运营商询问 domain 解析过程,所以从根本避免了劫持问题。
平均访问延迟下降:由于是 IP 直接访问省掉了一次 domain 解析过程,通过智能算法排序后找到最快节点进行访问。
用户连接失败率下降:通过算法降低以往失败率过高的服务器排序,通过时间近期访问过的数据提高服务器排序,通过历史访问成功记录提高服务器排序。
4.3 腾讯的HttpDNS思路
经过努力,腾讯公司的GSLB 团队推出的HttpDNS是为移动客户端量身定做的基于Http协议和域名解析的流量调度解决方案,专治LocalDNS解析异常以及流量调度不准。
详细介绍如下。
腾讯的HttpDNS基本原理:
HttpDNS的原理非常简单,主要有两步:
A、客户端直接访问HttpDNS接口,获取业务在域名配置管理系统上配置的访问延迟最优的IP。(基于容灾考虑,还是保留次选使用运营商LocalDNS解析域名的方式);
B、客户端向获取到的IP后就向直接往此IP发送业务协议请求。以Http请求为例,通过在header中指定host字段,向HttpDNS返回的IP发送标准的Http请求即可。
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地址等。
当然各位可能会问:用户将首选的域名解析方式切换到了HttpDNS,那么HttpDNS的高可用又是如何保证的呢?另外不同运营商的用户访问到同一个HttpDNS的服务IP,用户的访问延迟如何保证?
为了保证高可用及提升用户体验,HttpDNS通过接入了腾讯公网交换平台的BGP Anycast网络,与全国多个主流运营商建立了BGP互联,保证了这些运营商的用户能够快速地访问到HttpDNS服务;另外HttpDNS在多个数据中心进行了部署,任意一个节点发生故障时均能无缝切换到备份节点,保证用户解析正常。
接入效果及未来展望:
当前腾讯的HttpDNS方案已在腾讯内部接入了多个业务,覆盖数亿用户,并已持续稳定运行超过一年时间。而接入了HttpDNS的业务在用户访问体验方面都有了非常大的提升。
以某个接入HttpDNS的业务为例,该业务仅通过接入HttpDNS,在未做任何其它优化的情况下,用户平均访问延迟下降超过10%,访问失败率下降了超过五分之一,用户访问体验的效果提升非常显著。另外腾讯的HttpDNS服务除了在腾讯内部被广泛使用以外,也受到了业务同行的肯定。国内最大的publicDNS服务商114dns在受到腾讯DNS的启发下,也推出了HttpDNS服务。
在未来的日子里,腾讯GSLB团队将会在腾讯内部进一步推广HttpDNS服务,并将在实际业务的需求下对HttpDNS服务进行升级,如提供更为通用、安全、简单的接入协议,进一步提升接入用户的网络访问体验等等。希望HttpDNS能为各位在解决域名解析异常及全局流量调度失效方面提供一个简单、可行的思路。
5.对于现在现在最合适的解决法案
因为114和腾讯都有个免费的DNS服务所以我们可以用个第三方框架优先使用本地的DNS解析配合使用腾讯和114的免费DNS服务
最终使用的三方框架
七牛的HTTPDNS https://github.com/qiniu/happy-dns-android
5.1接入方式
implementation 'com.qiniu:happy-dns:0.2.13'
5.2添加工具类
public class HttpDns implements Dns {
private DnsManager dnsManager;
public HttpDns() {
IResolver[] resolvers = new IResolver[3];
try {
// 添加三个httpDns
resolvers[0] = AndroidDnsServer.defaultResolver();//localDns
resolvers[1] = new Resolver(InetAddress.getByName("114.114.114.114"));//114 httpDns
resolvers[2] = new DnspodFree();// 119.29.29.29 腾讯免费httpDns
dnsManager = new DnsManager(NetworkInfo.normal, resolvers);
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
@Override
public List lookup(String hostname) throws UnknownHostException {
if (dnsManager == null) //当构造失败时使用默认解析方式
return Dns.SYSTEM.lookup(hostname);
try {
String[] ips = dnsManager.query(hostname); //获取HttpDNS解析结果
if (ips == null || ips.length == 0) {
return Dns.SYSTEM.lookup(hostname);
}
List result = new ArrayList<>();
for (String ip : ips) { //将ip地址数组转换成所需要的对象列表
result.addAll(Arrays.asList(InetAddress.getAllByName(ip)));
}
return result;
} catch (IOException e) {
e.printStackTrace();
}
//当有异常发生时,使用默认解析
return Dns.SYSTEM.lookup(hostname);
}
}
5.3.因为现在所有项目都用的okhttp
okhttp添加配置
okHttpClient = builder
.dns(new HttpDns())
.build();
5.4.如何进行测试
使用模拟器修修改本地host文件把下载地址的域名指向一个不明ip 然后下载 (提示下载失败)
然后添加httpDns下载成功
接入成功