优化网络请求的一些方案

在当今的时代,我们一般都会使用移动端或者网页端通过 HTTP 请求等方式向一个或多个接口进行请求数据然后再进行处理。通常情况下,我们只需要管理好多线程进行并发请求以及对返回的结果进行处理就好了。但为了追求更好的用户体验,一般还会针对移动网络的特性做进一步优化。

在优化网络请求上一般我们会遇到三种情况:

  • 请求速度问题。
  • 弱网问题。
  • 安全问题。

请求速度问题

一个 HTTP 请求需要经过 DNS 解析拿到 IP、建立连接、发送和接收三个主要步骤才能完成,那么在提升请求速度上也是需要在这三个主要步骤进行优化。

DNS 优化

DNS 作为互联网的基础协议,其解析的速度似乎很容易被网站优化人员忽视。现在大多数新浏览器已经针对 DN S解析进行了优化,典型的一次 DNS 解析需要耗费 20-120 毫秒,减少 DNS 解析时间和次数是个很好的优化方式。DNS Prefetching 是让具有此属性的域名不需要用户点击链接就在后台解析,而域名解析和内容载入是串行的网络操作,所以这个方式能 减少用户的等待时间,提升用户体验。

默认情况下浏览器会对页面中和当前域名(正在浏览网页的域名)不在同一个域的域名进行预获取,并且缓存结果,这就是隐式的 DNS Prefetch。如果想对页面中没有出现的域进行预获取,那么就要使用显示的 DNS Prefetch 了。

浏览器上我们可以通过 HTML 标签的方式要求浏览器对 DNS 进行预解析:

<link rel="dns-prefetch" href="http://bdimg.share.baidu.com" />

那在移动端我们该如何进行处理以及我们有其它更好的方式吗?

首先用户的 DNS 是我们无法控制也无法保证一定是正常的,其次是运营商 LocalDNS 出口根据权威 DNS 目标 IP 地址进行 NAT,或将解析请求转发到其他 DNS 服务器,导致权威 DNS 无法正确识别运营商的 LocalDNS IP,引发域名解析错误、流量跨网。

这种情况下我们其实可以通过 HTTP 协议向私有的 DNS 服务器发送域名解析请求。替代了基于 DNS 协议向运营商 LocalDNS 发起解析请求的传统方式,可以避免 LocalDNS 造成的域名劫持和跨网访问的问题,解决移动互联网服务中域名解析异常带来的困扰,同时更有效地保障 App、小程序正常,避免移动互联网中的劫持、跨网域名解析错误等问题。

通过上面的方式用户实际访问是一个 IP 进行连接的。当然它也有不好的地方,HTTPS 可能会失效,无法对用户传输的数据进行很好的保障,会需要其它的加密手段进行进行保护。

其次是这种方式还有个特殊情况是 CDN 类的域名下如何进行处理。CDN 厂商必须要先支持通过 HTTP Header 中的 host 声明或其它字段声明来知道需要访问的是什么域名,其次 CDN 其实是有非常多节点的,HTTP DNS 解析服务必须要能够支持获取到用户的 IP 分析出地理位置、运营商后再将就近且同个运营商的 CDN 节点的 IP 返回给客户端。

开启 HTTP2

HTTP/2 是现行 HTTP 协议(HTTP/1.x)的替代,但它不是重写,HTTP 方法 / 状态码 / 语义都与 HTTP/1.x 一样。不过,HTTP/2 修改了数据格式化(分帧)以及在客户端与服务器间传输的方式。HTTP/2 基于 SPDY3,专注于性能,最大的一个目标是在用户和网站间只用一个连接。

HTTP/2 通过支持完整的请求与响应复用来减少延迟,通过有效压缩 HTTP 报头字段将协议开销降至最低,同时增加对请求优先级和服务器推送的支持。HTTP2 在用户和网站之间只用一个连接,避免后续建立连接过程中的几个往返和慢启动,同时减少了服务器的资源消耗。

使用 QUIC

QUIC (QUIC)是一种新的默认加密的互联网传输协议,它提供了许多改进,旨在加速 HTTP 传输并使其更加安全,其目标是最终取代网络上的 TCP 和 TLS。在这篇博客文章中,我们将概述 QUIC 的一些关键特性,以及它们如何使网络受益,还有支持这种激进的新协议所面临的一些挑战。详细的可以看我另外写的一篇文章:《QUIC 协议》。

Gzip

GZIP 最早由 Jean-loup Gailly 和 Mark Adler 创建,用于 UNⅨ 系统的文件压缩。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 GZIP 格式的。现今已经成为 Internet 上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

HTTP 协议上的 GZIP 编码是一种用来改进 WEB 应用程序性能的技术。大流量的 WEB 站点常常使用 GZIP 压缩技术来让用户感受更快的速度。这一般是指 WWW 服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.一般对纯文本内容可压缩到原大小的 40% .这样传输就快了,效果就是你点击网址后会很快的显示出来.当然这也会增加服务器的负载. 一般服务器中都安装有这个功能模块的。

目前市面上应该有很多 HTTP 服务器如 Nginx、Apache 等支持,浏览器也是支持自动解压的。可以极大缩小传输的内容大小。

Brotli

在 2015 年 9 月 Google 推出了无损压缩算法 Brotli。Brotli 通过变种的 LZ77 算法、Huffman 编码以及二阶文本建模等方式进行数据压缩,与其他压缩算法相比,它有着更高的压塑压缩效率。

根据 Google 发布的研究报告,Brotli 压缩算法具有多个特点,最典型的是以下 3 个:

  • 针对常见的 Web 资源内容,Brotli 的性能相比 Gzip 提高了 17-25%;
  • 当 Brotli 压缩级别为 1 时,压缩率比 Gzip 压缩等级为 9(最高)时还要高;
  • 在处理不同 HTML 文档时,Brotli 依然能够提供非常高的压缩率。

Protobuf

Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,目前已经正在使用的有超过 48,162 种报文格式定义和超过 12,183 个 .proto 文件。他们用于 RPC 系统和持续数据存储系统。

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API(即时通讯网注:Protobuf 官方工程主页上显示的已支持的开发语言多达10种,分别有:C++、Java、Python、Objective-C、C#、JavaNano、JavaScript、Ruby、Go、PHP,基本上主流的语言都已支持。

这个主要也是用于压缩传输内容,减少不必要传输的信息。一般是用在不支持 Gzip 的场景,比如是 WebSocket 等等。

HTTP transfer-encoding chunked

HTTP 协议的分块传输编码技术,服务端将响应内容 body 体分成若干个 chunk 块来分别传输的,表现为一边计算一边传输。当我们请求的内容是动态内容的话,这个时候服务端就无法预先得知动态内容的大小,怎么办? Transfer-Encoding: chunked 分块传输编码技术正是为此而生,这时服务端会在响应头中增加一个头字段: Transfer-Encoding: chunked,即切割成一块一块的数据块分段发送给客户端;另外注意有了这个字段,就用不到 Content-Length 字段了。

每次传输的体积本身就更小了,这种一般用于无法提前知道要传输的内容大小时的场景。

WebSocket 的 perMessageDeflate

WebSocket 中可以通过 perMessageDeflate 来对数据使用 deflate 算法进行压缩,deflate 是同时使用了 LZ77 算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。服务端和客户端如果都支持这个扩展的话,那么可以使用这种方式进行压缩传输数据。

弱网优化

这里参考微信开发者平台的建议
  • 在程序切到后台 5 秒后中断连接,并且提示用户重试。
  • 当客户端发生网络切换时,需要即时监听网络变化。

大部分的网络超时问题主要由弱网引起。弱网主要是基于以下现象判定:

  • 8 次网络请求中,出现 3 次以上连接超时。
  • 8 次网络请求中,出现 3 次 rtt 超过 400。
  • 8 次网络请求中,出现 3 次以上丢包。

弱网下其实可以配合 QUIC 协议来提升成功率。QUIC 协议支持预连接。QUIC的预连接,就是在进入弱网状态前提前建立QUIC连接。大家都知道 QUIC 引以为傲的 0RTT,但第一次建立连接的时候是需要 1RTT 的,客户端首先会向服务器发送一个 client hello 消息,服务器会回复一个 server reject 消息,这个消息中包括了server config,有了server config 后客户端就可以直接计算出密钥,完成 0RTT。通过上面的原理,客户端拉取 server config 的成功概率会直接影响 QUIC 在弱网下的流量,所以我们在 App 启动的过程中会做一次 QUIC 预连接,将 server config 拉取下来,这样等进入弱网后 alter 连接会大概率的竞争过原连接,进而走 QUIC 协议。

安全优化

标准协议 TLS 就已经保证了网络传输的安全,前身是 SSL,不断在演进,推荐用 TLS1.3。常见的 HTTPS 就是 HTTP 协议加上 TLS 安全协议。

TLS 1.3 与之前的协议有较大差异,主要在于:

  • 相比过去的的版本,引入了新的密钥协商机制 — PSK
  • 支持 0-RTT 数据传输,在建立连接时节省了往返时间
  • 废弃了 3DES、RC4、AES-CBC 等加密组件,废弃了 SHA1、MD5 等哈希算法
  • ServerHello 之后的所有握手消息采取了加密操作,可见明文大大减少
  • 不再允许对加密报文进行压缩、不再允许双方发起重协商
  • DSA 证书不再允许在 TLS 1.3 中使用

TLS 1.3 的握手不再支持静态的 RSA 密钥交换,这意味着必须使用带有前向安全的 Diffie-Hellman 进行全面握手。从上图可以看出,使用 TLS 1.3 协议只需要一次往返( 1-RTT )就可以完成握手。

相比 TLS 1.2,TLS 1.3 的握手时间减半。这意味着访问一个移动端网站,使用 TLS 1.3 协议,可能会减少将近 100ms 的时间。同时 TLS 1.3 带来了更强大的安全性,何乐而不为呢?