一、前言

Hi,大家好,这里是承香墨影!

当我们谈到优化时,一个通用的思路是将待优化的功能,拆分成一个个小的环节,再每个环节单独去分析有没有可以优化的点,App 的网络优化也是如此。

但说到网络优化,整个链路非常的长,中间的涉及到客户端 & 服务端,流程非常的长。在 App 侧,只占整个链路极小的一段,可以优化的无非也就是 2 点。

  1. 加快连接的速度。例如使用 HTTPDNS、升级 TLS 协议版本等;

  2. 减少请求的次数。例如增加 HTTP 缓存;

本文的主题,就是利用 HTTPDNS 来优化网络速度。

在 App 访问网络的时候,DNS 解析是网络请求的第一步,默认使用的是运营商的 LocalDNS 服务。有数据统计,在这一块 3G 网络下,耗时在 200~300ms,4G 网络下也需要 100ms。

当然在 5G 的耗时应该更低,但是 5G 现在并没有普及,多数情况下我们还是看 4G 和 Wifi 场景下的网络情况。

解析慢,并不是 LocalDNS 最大的问题,它还存在一些更为严重的问题,例如:DNS 劫持、DNS 调度不准确(缓存、转发、NAT)导致性能退化等等,这些才是网络优化最应该解决的问题。

想要优化 DNS,现在最简单成熟的方案,就是使用 HTTPDNS。

今天就来聊聊,DNS、HTTPDNS,以及在 Android 下,如何使用 OKHttp 来集成 HTTPDNS。

二、DNS 和 HTTPDNS

2.1 什么是 DNS

在说 HTTPDNS 之前,先简单了解一下什么是 DNS?

网络的世界中,每个有效的域名背后,都有为其提供服务的服务器,而我们网络通信的第一步,就是通过域名,解析出域名背后的服务器 IP 地址。

对于大多数普通人而言,记住一串有意义的域名(网址),肯定是比记忆 IP 地址简单。如果有某种方法,可以通过域名,查到其提供服务的服务器 IP 地址,那就非常方便了。这里就需要用到 DNS 服务器以及 DNS 解析。

DNS(Domain Name System),它的作用就是根据域名,查出对应的 IP 地址,它是 HTTP 协议的前提。只有将域名正确的解析成 IP 地址后,后面的 HTTP 流程才可以继续进行下去。

既然 DNS 服务如此重要,对于 DNS 服务器的要求,一定是高可用、高并发和分布式的服务器。

它被分为多个层次结构。

  • 根 DNS 服务器:返回顶级域 DNS 服务器的 IP 地址;

  • 顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址;

  • 权威 DNS 服务器:返回相应主机的 IP 地址;

这三类 DNS 服务器,类似一种树状的结构,分级构成。

当开始 DNS 解析的时候,如果 LocalDNS 没有缓存,那就会向 LocalDNS 服务器请求(通常就是运营商),如果还是没有,就会一级一级的,从根域名查对应的顶级域名,再从顶级域名查权威域名服务器,最后通过权威域名服务器,获取具体域名对应的 IP 地址。

DNS 在提供域名和 IP 地址映射的过程中,其实提供了很多基于域名的功能,例如服务器的负载均衡,但是它也带来了一些问题。

2.2 DNS 的问题

DNS 的细节还有很多,本文就不展开细说了,其问题总结来说就是以下 3 点。

1. 不稳定

DNS 劫持或者故障,导致服务不可用。

2. 不准确

LocalDNS 调度,并不一定是就近原则。

某些小运营商没有 DNS 服务器,会直接调用其他运营商的 DNS 服务器,最终导致跨网传输。例如:用户侧是移动运营商,调度到了电信的 IP,造成访问慢,甚至访问受限等问题。

3. 不及时

运营商还可能会修改 DNS 的 TTL(Time-To-Live,DNS 缓存时间),导致 DNS 的修改,延迟生效。

还有运营商为了保证网内用户的访问质量,同时减少跨网结算,运营商会在网内搭建内容缓存服务器,通过把域名强行指向内容缓存服务器的地址,来实现本网流量完全留在本地的目的。

对此,不同运营商的策略都可能不一致,这对我们来说,就是一个黑匣子,中间有太多不可控的问题。

正是因为 LocalDNS 存在种种问题,所以引出了 HTTPDNS。

2.3 HTTPDNS 的解决方案

DNS 不仅支持 UDP,它还支持 TCP,但是大部分标准的 DNS,都是基于 UDP 与 DNS 服务器的 53 端口进行交互。

HTTPDNS 则不同,顾名思义它是利用 HTTP 协议,与 NDS 服务器的 80 端口进行交互。不走传统的 DNS 解析,从而绕过运营商的 LocalDNS 服务器,有效的防止了域名劫持,提高域名解析的效率。

这就相当于,每家各自基于 HTTP 协议,自己实现了一套域名解析,自己去维护了一份域名与 IP 的地址簿,而不是使用同一的地址簿(DNS 服务器)。

据说微信有自己部署的 NETDNS,而各大云服务商,阿里云和腾讯云也提供了自己的 HTTPDNS 服务,对于我们普通开发者,只需要付出少量的费用,在手机端嵌入支持 HTTPDNS 的客户端 SDK,即可使用。

三、 OKHttp 接入 HTTPDNS

既然了解了 HTTPDNS 的重要性,接下来看看,如何在 OkHttp 中集成 HTTPDNS。

OkHttp 是一个处理网络请求的开源项目,是 Android 端最火热的轻量级网络框架。在 OkHttp 中,默认是使用系统的 DNS 服务 InetAddress 进行域名解析。

InetAddress ip2= InetAddress.getByName("www.cxmydev.com");
System.out.println(ip2.getHostAddress());
System.out.println(ip2.getHostName());

而想在 OkHttp 中使用 HTTPDNS,有两种方式。

  1. 通过拦截器,在发送请求之前,将域名替换为 IP 地址;

  2. 通过 OkHttp 提供的 .dns() 接口,配置 HTTPDNS;

对这两种方法来说,当然是推荐使用 OkHttp 提供的 dns() 这个标准 API 来实现了。拦截器的方式,也建议有所了解,实现很简单,但是有坑。

3.1 拦截器接入方式

1. 拦截器接入

拦截器是 OkHttp 中,非常强大的一种机制,它可以在请求和响应之间,做一些我们的定制操作。

在 OkHttp 中,可以通过实现 Interceptor 接口,来定制一个拦截器。使用时,只需要在 OkHttpClient.Builder 中,调用 addInterceptor() 方法来注册此拦截器即可。

OkHttp 的拦截器不是本文的重点,我们还是回到拦截器去实现 HTTPDNS 的话题上,拦截器没什么好说的,直接上相关代码。

class HTTPDNSInterceptor : Interceptor{override fun intercept(chain: Interceptor.Chain): Response {val originRequest = chain.request()val httpUrl = originRequest.url()val url = httpUrl.toString()val host = httpUrl.host()val hostIP = HttpDNS.getIpByHost(host)val builder = originRequest.newBuilder()if(hostIP!=null){builder.url(HttpDNS.getIpUrl(url,host,hostIP))builder.header("host",hostIP)}val newRequest = builder.build()val newResponse = chain.proceed(newRequest)return newResponse}
}

在拦截器中,使用 HttpDNS 这个帮助类,通过 getIpByHost() 将 Host 转为对应的 IP。

如果通过抓包工具抓包,你会发现,原本的类似 http://www.cxmydev.com/api/user 的请求,被替换为:http://220.181.57.xxx/api/user

2. 拦截器接入的坏处

使用拦截器,直接绕过了 DNS 的步骤,在请求发送前,将 Host 替换为对应的 IP 地址。

这种方案,在流程上很清晰,没有任何技术性的问题。但是这种方案存在一些问题,例如:HTTPS 下 IP 直连的证书问题、代理的问题、Cookie 的问题等等。

其中最严重的问题是,此方案(拦截器 + HTTPDNS)遇到 HTTPS 时,如果存在一台服务器支持多个域名,可能导致 SSL/TLS 证书无法匹配的问题。

在说到这个问题之前,就要先了解一下 HTTPS 和 SNI。

HTTPS 是为了保证安全的,在发送 HTTPS 请求之前,首先要进行 SSL/TLS  握手,握手的大致流程如下:

  1. 客户端发起握手请求,携带随机数、支持算法列表等参数;

  2. 服务端根据请求,选择合适的算法,下发公钥证书和随机数;

  3. 客户端对服务端证书,进行校验,并发送随机数信息,该信息使用公钥加密;

  4. 服务端通过私钥获取随机数信息;

  5. 双方根据以上交互的信息,生成 Session Ticket,用作该连接后续数据传输的加密密钥;

在这个流程中,客户端需要验证服务器下发的证书。首先通过本地保存的根证书解开证书链,确认证书可信任,然后客户端还需要检查证书的 domain 域和扩展域,看看是否包含本次请求的 HOST。

在这一步就出现了问题,当使用拦截器时,请求的 URL 中,HOST 会被替换成 HTTPDNS 解析出来的 IP。当服务器存在多域名和证书的情况下,服务器在建立 SSL/TLS 握手时,无法区分到底应该返回那个证书,此时的策略可能返回默认证书或者不返回,这就有可能导致客户端在证书验证 domain 时,出现不匹配的情况,最终导致 SSL/TLS  握手失败。

这就引发出来 SNI 方案,SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的 SSL/TLS 扩展。

SNI 的工作原理,在连接到服务器建立 SSL 连接之前,先发送要访问站点的域名(hostname),服务器根据这个域名返回正确的证书。现在,大部分操作系统和浏览器,都已经很好的支持 SNI 扩展。

3. 拦截器 + HTTPDNS 的解决方案

这个问题,其实也有解决方案,这里简单介绍一下。

针对「domain 不匹配」的问题,可以通过 hook 证书验证过程中的第二步,将 IP 直接替换成原来的域名,再执行证书验证。

而 HttpURLConnect,提供了一个 HostnameVerifier 接口,实现它即可完成替换。

public interface HostnameVerifier {public boolean verify(String hostname, SSLSession session);
}

如果使用 OkHttp,可以参考 OkHostnameVerifier (source://src/main/java/okhttp3/internal/tls/OkHostnameVerifier.java) 的实现,进行替换。

本身 OkHttp 就不建议通过拦截器去做 HTTPDNS 的支持,所以这里就不展开讨论了,这里只提出解决的思路,有兴趣可以研究研究源码。

3.2 OKHttp 标准 API 接入

OkHttp 其实本身已经暴露了一个 Dns 接口,默认的实现是使用系统的 InetAddress 类,发送 UDP 请求进行 DNS 解析。

我们只需要实现 OkHttp 的 Dns 接口,即可获得 HTTPDNS 的支持。

在我们实现的 Dns 接口实现类中,解析 DNS 的方式,换成 HTTPDNS,将解析结果返回。

class HttpDns : Dns {override fun lookup(hostname: String): List<InetAddress> {val ip = HttpDnsHelper.getIpByHost(hostname)if (!TextUtils.isEmpty(ip)) {//返回自己解析的地址列表return InetAddress.getAllByName(ip).toList() } else {// 解析失败,使用系统解析return Dns.SYSTEM.lookup(hostname)}}
}

使用也非常的简单,在 OkHttp.build() 时,通过 dns() 方法配置。

mOkHttpClient = httpBuilder.dns(HttpDns()).build();

这样做的好处在于:

  1. 还是用域名进行访问,只是底层 DNS 解析换成了 HTTPDNS,以确保解析的 IP 地址符合预期;

  2. HTTPS 下的问题也得到解决,证书依然使用域名进行校验;

OkHttp 既然暴露出 dns 接口,我们就尽量优先使用它。

四、小结时刻

现在大家知道,在做 App 的网络优化的时候,第一步就是使用 HTTPDNS 优化 DNS 的步骤。

所有的优化当然是以最终效果为目的,这里提两条大厂公开的数据,对腾讯的产品,在接入 HTTPDNS 后,用户平均延迟下降超过 10%,访问失败率下降超过五分之一。而百度 App 的 Feed 业务,Android 劫持率由 0.25% 降低到 0.05%。

此种优化方案,非常依赖 HTTPDNS 服务器,所以建议使用 阿里云、腾讯云 这样相对稳定的云服务商。

本文对你有帮助吗?留言、点赞、转发是最大的支持,谢谢!

references:

【1】百度 App 网络深度优化系列一 DNS 优化

【3】鹅厂网事,HTTPDNS 服务详解

-- End --

本文对你有帮助吗?留言、转发、点好看是最大的支持,谢谢!

推荐阅读:

Android 架构,拒绝生搬硬套!

三句话,让面试官觉得我懂 SparseArray!

Android 的 Window 如何理解?Dialog 到底是不是子窗口?

百度技术:“App 优化网络,先从 HTTPDNS 开始” | 原理到实战相关推荐

  1. 弱网搭建及模拟工具,弱网或无网状态下 App的优化,弱网优化,网络优化(DNS/HttpDNS)

    现代移动端网络短连接的优化手段总结:请求速度.弱网适应.安全保障- http://www.52im.net/thread-1413-1-1.html 快速理解P2P技术中的NAT穿透原理- http: ...

  2. APP弱网络条件下,体验优化之道

    APP弱网络条件下,体验优化之道 最近跟朋友聊天刚好聊到这一块,他们是在做电商业务,商品图片及其多,API接口请求频率也高.然而,他们在移动2/3G的网络环境下,APP经常会出现Loading很久的情 ...

  3. Windows平台基于API Hook技术的WinInet网络库HttpDNS实现方案

    一.项目背景 学而思网校直播课堂在线安装程序,是一个独立的应用程序,提供学生端的安装功能,为了减少安装包体积,避免引入第三方网络库,使用的是操作系统的WinInet网络库.为了更好的优化网络,提高网络 ...

  4. php百度优化,百度技术沙龙第 24 期 PHP 性能优化实践

    本文作者:HelloDeveloper 在 3 月 10 日由 @百度主办.@InfoQ 策划组织和实施的第 24 期百度技术沙龙活动上,来自百度 PHP 高级顾问,PHP 语言开发组成员惠新宸(@l ...

  5. 全面解析Android性能优化,含腾讯、阿里、百度、京东、美团、爱奇艺等大厂实战解析

    前言 安卓开发大军浩浩荡荡,经过十多年的发展.红利期已过,现在是增量有限,存量厮杀,从争夺用户到争夺时长.不管是用户也好.企业也好,都对 App 的用户体验和性能提出了更高的要求. 如果你已经有 2 ...

  6. Android App优化:内存优化、电量优化、网络优化等 (2)

    -- 优化专题: 整理一系列的专题:比如APK瘦身.插件化.程序架构.性能优化.自定义view.增量升级.移动开发各种技术解决方案等.   Android后期发展的五大趋势:一.性能优化:二.高级UI ...

  7. vue 同一个域名移动端pc端两套代码跳转_百度移动搜索优化:手机站优化指南

    百度移动搜索是全球最大的中文手机搜索引擎,每天用户使用百度移动搜索发起的搜索请求高达数亿次.本文档旨在为手机站站长提供官方.明确的搜索引擎优化标准,使站长合理.可持续的提升手机站流量,获得长久.稳定的 ...

  8. 百度seo关键词优化 优化技巧是什么

    百度seo关键词优化是指把网站优化到排名的效果,那么大家知道百度seo关键词的分类吗?优化技巧是什么呢?优化网站目的是什么呢?下面宝丞网小编给大家介绍一下相关的内容. 百度seo关键词优化 关键词优化 ...

  9. 百度超级链开放网络治理监督藏品火爆售罄!

    11月25日,8万多个百度超级链开放网络治理监督藏品提前售罄.这是百度超级链首次开放通道邀请生态伙伴成为监督节点,吸引大量关注. 此次发行的藏品基于百度超级链开放网络设计,开放网络(XuperOS)是 ...

最新文章

  1. 洛谷 P1843 奶牛晒衣服
  2. @springbootapplication 注解_Spring Boot最核心的27个干货注解,你了解多少?
  3. 快速了解和使用Photon Server
  4. 微信终端跨平台组件 mars 系列(二) - 信令传输超时设计
  5. java中system_《java中System类》 | 学步园
  6. 复制vmware overLay网络无法ping通 ping www.baidu.com可以
  7. 改进了一下这个游戏的输出及思路,是不是好玩多了??:)
  8. 飞龙的程序员书单 – 编程语言
  9. tomcat 日志配置
  10. FZU 2129 子序列个数(DP)题解
  11. linux系统pdf目录编辑器,Linux操作系统(三)编辑器与实用程序.pdf
  12. SPSS中的数据分析—信度效度检验【1】
  13. STM32F103标准库开发---目录
  14. kubeadm工作原理-kubeadm init原理分析-kubeadm join原理分析
  15. 开发者工具的暖心提示语
  16. python散点图图例显示标记点类型_python – matplotlib散点图中的标记点
  17. 心田花开小学拼音复韵母ao、ou、iu读法教程
  18. loadrunner入门教程(33) -- Analysis图
  19. 重新定义超频 解读NVIDIA的Shader频率
  20. 海伦公式(根据三角形三条边求面积)

热门文章

  1. 每周一喂丨网络黑灰产工具“八大样儿”
  2. java if经典程序_java经典程序题15道(另附自己做的答案)
  3. linux 下文件防篡改,Rsync+inotify实现文件防篡改
  4. linux笔记本触屏方向反了,[已解决]如何设置触摸板上下滚屏反转(像mac一样,和windows是反的)...
  5. 关于啤酒和尿布故事的真相
  6. pcb板中字母P代表什么
  7. 国内TCP单边“加速”的“正确”做法
  8. wps导航栏在哪设置?
  9. 删除K个数字,使剩下的数字串最大(最大数字)
  10. Pannel DB支持定时文件、数据库备份、系统防御、监控告警、中间件安装