前言

本章节主要讲解下 网络如何进行优化;
网络请求大致的实际流程:
- 根据域名进行DNS解析(域名转换为IP 就是DNS解析,udp协议)
- IP
- Socket 连接(TCP/IP的三次握手)
- 根据 Http 报文格式 将数据进行封装,然后根据与服务器建立的 socket 获取到 OutputStream 把我们的 Http 报文发给服务端,这样就完成了 Http 的请求;
在这个过程中有哪些可以优化的流程呢,大致可以分为以下几个方向
- DNS 优化
- 连接优化
- 数据优化
- 图片优化
- 缓存优化
- 安全优化
- 其他优化
DNS 优化
要了解 DNS 如何优化,我们需要先来了解下传统 DNS 的解析流程;
传统 DNS 解析流程

DNS(Domain Name System),它的作用是根据域名查出 IP 地址,它是 HTTP 协议的前提,只有将域名正确的解析成 IP 地址后,后面的 HTTP 流程才能进行;
DNS 完整的解析流程很长,会先从本地系统缓存(运营商localDNS)取,若没有就到最近的 DNS 服务器取,若没有再到主域名服务器取,每一层都有缓存,但为了域名解析的实时性,每一层缓存都有过期时间。
传统的 DNS 解析都是使用的 UDP 协议;传统的 DNS 解析有以下几个缺点:
- 缓存时间设置过长,因为是优先请求运营商本地 DNS,而运营商的 localDNS 有缓存,导致 DNS 更新不及时,如果缓存设置过短,那么就会像目标服务器进行请求,大量 DNS 解析请求影响请求速度;运营商的 LocalDNS 服务。这块耗时在 3G 网络下可能是 200~300ms,4G 网络也需要 100ms;
- 不稳定性,域名劫持,例如 访问 www.baidu.com 结果返回的是一个充值页面;UDP 协议,无状态,容易域名劫持(难复现、难定位、难解决),每天至少几百万个域名被劫持,一年至少十次大规模事件;
- 不准确性,DNS解析过程不受控制,无法保证解析到最快的IP,比如:请求者在湖南,结果返回了新疆的ip 这样就会影响请求速度,而如果返回就近的ip则会提升请求速度;LocalDNS 调度经常出现不准确,比如北京的用户调度到广东 IP,移动的运营商调度到电信的 IP,跨运营商调度会导致访问慢,甚至访问不了;
- 一次请求只能解析一个域名;
- 不及时性;运营商可能会修改 DNS 的 TTL,导致 DNS 修改生效延迟。不同运营商的服务实现不一致,我们也很难保证 DNS 解析的耗时;
那么如何进行优化呢? 使用 HttpDNS
HttpDNS
原理:自己做域名解析的工作(使用 http 协议来进行 DNS 的解析工作),通过 Http 请求后台去拿到域名对应的 ip 地址;
优点:
- 由于 HttpDns 是通过 IP 直接请求 http 获取服务器 A 记录的地址,不存在向本地运营商请求 domain 解析的过程,从根本上避免了劫持问题;
- DNS 解析由自己控制,可以确保根据用户所在地返回就近的 IP 地址,或根据客户端测速结果使用最快的 IP;
- 一次请求可以解析多个域名;
这里以阿里的 HttpDNS 为例子,讲一下如何接入;
阿里 HttpDNS
文档链接:Android端HTTPDNS+OkHttp接入指南
接入指南:help.aliyun.com/document_de...
百度 HttpNDS
只连 IP
设置 header 的时候 设置 host,将 host 直接改成 IP;
连接优化
本质就是:Keep-alive(连接复用)
Keep-alive 原理是:请求完成后不立即释放连接,而是放入连接池中,若这时有另一个请求要发出,请求的域名和端口是一样的,就直接拿出连接池中的连接进行发送和接收数据,减少了建立连接的耗时;
Http 协议中的一个请求头,HTTP 1.1 默认开启,一定程度上缓解了每次请求都要进行 TCP 三次握手建立连接的耗时;
如何开启:客户端在请求头中 添加 Connection Keep-Alive;
开启之后,说明 客户端希望与服务端保持一个长久的连接,HTTP 底层是 TCP/IP 而 TCP/IP 可以用 socket 建立连接,所以 HTTP 其实就用客户端和服务端用 socket 建立的一个连接,然后用 socket 按照 Http 协议的格式来收发数据;
HTTP 1.1之后新增的 keep-alive 不是客户端想保持长连接就能保持的,如果服务端回复的是 keep-alive 那么可以保持,如果 服务端回复的是 close 那么就会断开这个长连接;
连接复用的优点:
本质上是这个 socket 可以被用来复用,这样就不用每次都经历握手的操作,加快通信的效率;
连接复用的缺点:
-
Http 1.1一次只能发送一个请求
1)串行发送,可以一直复用一个连接,但速度很慢,每个请求都要等待上个请求结束之后才能发送;
2)并行发送,那么只能每个请求都要进行 TCP 的三次握手建立连接;
连接复用的缺点如何改良:多路复用;
Http2 提出了『多路复用』去解决,复用的连接支持同时处理多条请求,所有的请求都可以并发到这个连接上进行;
多路复用原理图如下:

原理就是:把传输的数据封装成一个一个的 stream,每个 stream 都有标识,stream 的发送和接收可以是乱序的,不依赖顺序,也就不会有阻塞问题,接收端可以根据 stream 的标识来区分属于哪个请求,再进行数据拼接,得到最终的数据;
简单来说就是:同时发起两个请求,这两个请求复用同一个 socket,这就叫多路复用规则;

Http 2 的缺点:
- 一个是同一条 H2 连接只支持同一个域名;
- 一个是后端支持 HTTP/2.0 需要额外的改造;
如何解决:

我们只需要在统一接入层做改造,接入层将数据转换到 HTTP/1.1 再转发到对应域名的服务器;
数据优化
来聊数据优化的时候,我们需要看下数据对网络请求速度的影响的因素有哪些?一个是:压缩率;一个是:解压序列化反序列化的速度;
优化方向:
- 请求头 header;
- 请求 URL;
- 请求体 body;
请求头 header
- 对于 header 来说,如果使用 HTTP/2.0 连接本身的头部压缩技术;
请求 URL
- 对于请求 URL 来说,一般会带很多的公共参数,这些参数大部分都是不变的。这样不变的参数客户端只需要上传一次即可,其他请求我们可以在接入层中进行参数扩展;
请求体 body
-
数据序列化
- protobuf,优点:二进制,数据更加的紧凑,都压缩后 也会比json小,序列化速度也比json快,缺点需要后台支持
- json
-
数据压缩
- gzip数据压缩
- Accept-Encoding : gzip;客户端支持服务端将数据进行 gzip 压缩之后传递给客户端
- Google 的Brotli
- Facebook 的Z-standard
- gzip数据压缩
protobuf 使用
-
引入 lite 版本的 protobuf 组件
-
创建 proto 文件

- project下的 build.gradle 引入 protobuf 插件

- app.build 下 引入插件并进行相关配置

- build之后 就会生成对应的java文件

gzip 使用
开启 gzip 压缩
服务器响应 gzip 压缩
客户端发送 gzip 压缩之后的数据
图片优化
优化手段
不同网络类型,下发不同图片
每一次请求的请求头 都加上网络类型和网络信号,来获取不同网络状态下的图片;
使用 WebP 替换 PNG 图片

缓存优化
优化手段
开启 Http 缓存
设置『缓存路径』和『缓存空间』
模拟无网、弱网环境
模拟断网
模拟超时
模拟限速
安全优化
优化手段:HTTPS
HTTPS
连接复用率
通过多个域名共用同一个 HTTP/2 连接、长连接等方式提升连接复用率
减少握手次数
TLS 1.3 可以实现 0-RTT 协商,事实上在 TLS 1.3 release 之前,微信的 mmtls、Facebook 的 fizz、阿里的 SlightSSL 都已在企业内部大规模部署;
性能提升
使用 ecc 证书代替 RSA,服务端签名的性能可以提升 4~10 倍,但是客户端校验性能降低了约 20 倍,从 10 微秒级降低到 100 微秒级。另外一方面可以通过 Session Ticket 会话复用,节省一个 RTT 耗时;
好了,网络优化就写到这里吧~
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我最大的动力~