HttpDNS功能说明及实现
功能说明
HTTPDNS使用HTTP协议进行域名解析,代替现有基于UDP的DNS协议,域名解析请求直接发送到阿里云的HTTPDNS服务器,从而绕过运营商的Local DNS,能够避免Local DNS造成的域名劫持问题和调度不精准问题。
| 防劫持 | 绕过运营商Local DNS,避免域名劫持,让每一次访问都畅通无阻。 |
| 精准调度 | 基于访问的来源IP,获得最精准的解析结果,让客户端就近接入业务节点。 |
| 0ms解析延迟 | 通过热点域名预解析、缓存DNS解析结果、解析结果懒更新策略等方式实现0解析延迟 |
| 快速生效 | 避免Local DNS不遵循权威TTL,解析结果长时间无法更新的问题 |
| 降低解析失败率 | 有效降低无线场景下解析失败的比率 |
防劫持
HTTPDNS代替了传统的LocalDNS完成递归解析的功能,基于HTTP协议的设计可以适用于几乎所有的网络环境,同时保留了鉴权、HTTPS等更高安全性的扩展能力,避免恶意攻击劫持行为。
精准调度
传统域名解析的调度精准性问题,本质根源在于Local DNS的部署和分配机制上。由于碎片化的管理方式,这些环节的服务质量同样很难得到保障。HTTPDNS在递归解析实现上优化了与权威DNS的交互,通过<a name="https://datatracker.ietf.org/doc/rfc7871">edns-client-subnet</a>协议将终端用户的IP信息直接交付给权威DNS,这样权威DNS就可以忽略Local DNS IP信息,根据终端用户的IP信息进行精准调度,避免Local DNS的坐标干扰
DNS解析0延迟:
-
构建客户端DNS缓存;
通过合理的DNS缓存,我们确保每次网络交互的DNS解析都是从内存中获取IP信息,从而大幅降低DNS解析开销。根据业务的不同,我们可以制订更丰富的缓存策略,如根据运营商缓存,可以在网络切换的场景下复用已缓存的不同运营商线路的域名IP信息,避免网络切换后进行链路重选择引入的DNS网络解析开销。另外,我们还可以引入IP本地化离线存储,在客户端重启时快速从本地读取域名IP信息,大幅提升首页载入效率。 -
热点域名预解析;
在客户端启动过程中,我们可以通过热点域名的预解析完成热点域名的缓存载入。当真正的业务请求发生时,直接由内存中读取目标域名的IP信息,避免传统DNS的网络开销。 -
懒更新策略;
绝大多数场景下业务域名的IP信息变更并不频繁,特别是在单次APP的使用周期内,域名解析获取的IP往往是相同的(特殊业务场景除外)。因此我们可以利用DNS懒更新策略来实现TTL过期后的DNS快速解析。所谓DNS懒更新策略即客户端不主动探测域名对应IP的TTL时间,当业务请求需要访问某个业务域名时,查询内存缓存并返回该业务域名对应的IP解析结果。如果IP解析结果的TTL已过期,则在后台进行异步DNS网络解析与缓存结果更新。通过上述策略,用户的所有DNS解析都在与内存交互,避免了网络交互引入的延迟。
实现方案
服务端:
服务端提供API接口,app端直接通过ip地址访问,ip地址可以有多个
请求方式:HTTP GET
URL参数说明:
| host | 必须 | 要解析的域名 |
| ip | 可选 | 用户的来源IP,如果没指定这个参数,默认使用请求连接的源IP |
请求示例:
-
示例1(默认来源IP):http://x.x.x.x/d?host=www.suning.com
-
示例2(指定来源IP):http://x.x.x.x/d?host=www.suning.com&ip=185.53.179.6
考虑到服务IP防攻击之类的安全风险,为保障服务可用性,HTTPDNS同时提供多个服务IP,当某个服务IP在异常情况下不可用时,可以使用其它服务IP进行重试。
请求成功时,HTTP响应的状态码为200,响应结果用JSON格式表示,示例如下:
{"host": "www.suning.com","ips": ["112.84.104.48"],"ttl": 57,"origin_ttl": 120 }请求失败的响应示例:
{"code": "MissingArgument" }错误码列表如下:
| MissingArgument | 400 | 缺少必要参数 |
| InvalidHost | 400 | 域名格式不合法 |
| MethodNotAllowed | 405 | 不支持的HTTP方法 |
| InternalError | 500 | 服务端内部错误 |
错误处理
异常下的出错兼容逻辑,主要包括异步请求,重试,降级
异步请求
访问HTTPDNS服务时,应该使用异步请求的策略,避免解析延迟太大而对业务造成影响,特别是在网络环境异常或HTTPDNS服务IP异常不可 用时,如果用同步访问,需要等待网络超时后才会返回解析失败,这个超时时间较大,可能对业务的使用体验造成很大影响。异步请求策略:解析域名时,如果当前缓存中有TTL未过期的IP,可直接使用;如果没有,则立刻让此次请求降级走原生LocalDNS解析,同 时另起线程异步地发起HTTPDNS请求进行解析,更新缓存,这样后续解析域名时就能命中缓存。重试
访问HTTPDNS服务解析域名时,如果请求HTTPDNS服务端失败,即HTTP请求没有返回,可以进行重试。大部分情况下,这种访问失败是由于网络原因引起的,重试可以解决。降级
不管是因为什么原因,当通过HTTPDNS服务无法获得域名对应的IP时,都必须降级:使用标准的DNS解析,通过Local DNS去解析域名。Android端:
OkHttp默认使用系统DNS服务InetAddress进行域名解析,但同时也暴露了自定义DNS服务的接口,通过该接口我们可以优雅地使用HttpDns。
- 自定义DNS接口
OkHttp暴露了一个Dns接口,通过实现该接口,我们可以自定义Dns服务:
public class OkHttpDns implements Dns {private static final Dns SYSTEM = Dns.SYSTEM;HttpDnsService httpdns;//httpdns 解析服务private static OkHttpDns instance = null;private OkHttpDns(Context context) {this.httpdns = HttpDns.getService(context, "account id");}public static OkHttpDns getInstance(Context context) {if(instance == null) {instance = new OkHttpDns(context);}return instance;}@Overridepublic List<InetAddress> lookup(String hostname) throws UnknownHostException {//通过异步解析接口获取ipString ip = httpdns.getIpByHostAsync(hostname);if(ip != null) {//如果ip不为null,直接使用该ip进行网络请求List<InetAddress> inetAddresses = Arrays.asList(InetAddress.getAllByName(ip));Log.e("OkHttpDns", "inetAddresses:" + inetAddresses);return inetAddresses;}//如果返回null,走系统DNS服务解析域名return Dns.SYSTEM.lookup(hostname);} }- 创建OkHttpClient
创建OkHttpClient对象,传入OkHttpDns对象代替默认Dns服务:
private void okhttpDnsRequest() {OkHttpClient client = new OkHttpClient.Builder().dns(OkHttpDns.getInstance(getApplicationContext())).build();Request request = new Request.Builder().url("http://www.aliyun.com").build();Response response = null;client.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);DataInputStream dis = new DataInputStream(response.body().byteStream());int len;byte[] buff = new byte[4096];StringBuilder result = new StringBuilder();while ((len = dis.read(buff)) != -1) {result.append(new String(buff, 0, len));}Log.d("OkHttpDns", "Response: " + result.toString());}}); }- 总结
相比于通用方案,OkHttp+HttpDns有以下两个主要优势:
实现简单,只需通过实现Dns接口即可接入HttpDns服务通用性强,该方案在HTTPS,SNI以及设置Cookie等场景均适用。规避了证书校验,域名检查等环节IOS端:
基于NSURLProtocol可拦截iOS系统上基于上层网络库NSURLConnection/NSURLSession发出的网络请求;
通过以下接口注册自定义NSURLProtocol,用于拦截上层网络请求,并创建新的网络请求接管数据发送、接收、重定向等处理逻辑,将结果反馈给原始请求。
[NSURLProtocol registerClass:[CustomProtocol class]];自定义NSURLProtocol处理过程概述:
- 在canInitWithRequest中过滤要需要做HTTPDNS域名解析的请求;
- 请求拦截后,做HTTPDNS域名解析;
- 解析完成后,同普通请求一样,替换URL.host字段,替换HTTP Header Host域,并接管该请求的数据发送、接收、重定向等处理;
作者:刚哥说
链接:https://www.jianshu.com/p/15ff2aeb5b5b
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
总结
以上是生活随笔为你收集整理的HttpDNS功能说明及实现的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: Syslog系统日志配置
- 下一篇: HttpDns 原理是什么