访问 Web 网站和应用时,经常会遇到各种各样的性能问题。比如网页加载慢、视频卡、网络出错等,其中一个关键的影响因素就是网络协议。本文会系统化地介绍 TCP、UDP、HTTP1.1、HTTPS(包括最新的 TLS1.3 协议)、SPDY、HTTP2 等协议存在的问题,以及如何在特定的场景下通过网络…
访问 Web 网站和应用时,经常会遇到各种各样的性能问题。比如网页加载慢、视频卡、网络出错等,其中一个关键的影响因素就是网络协议。本文会系统化地介绍 TCP、UDP、HTTP1.1、HTTPS(包括最新的 TLS1.3 协议)、SPDY、HTTP2 等协议存在的问题,以及如何在特定的场景下通过网络协议的优化实现访问速度的提升。
注:本文整理自腾讯 TEG 基础架构部高级工程师罗成在 ArchSummit 2017 深圳站上的演讲。
前言:WEB 性能优化的问题
提到 WEB 性能优化,那也就意味着我们遇到了很多性能方面的问题。
Web 加速,协议先行!
如上图所示,看到这一行字,我相信大家的脑海里一定会浮现出很多的场景画面,比如说无法接入网络,又比如说页面一直在加载,或者说内容可能一直卡在 99% 加载不出来,包括视频的卡顿、短视频以及直播,还有从 wifi 切到 4G 网络出现重新加载的情况,那遇到这么多的 web 问题,其主要原因是什么呢?
我进行简单罗列一下,归纳为有以下两种情况。
Web 加速,协议先行!
直接和页面相关的页面大小,页面的元素类型的多少,也就是说一个页面越大,元素越多,动态的交互越频繁,那相应的性能可能就会受到更大影响。页面优化问题也是前端优化的主战场。
网络环境以及端配置,包括用户手机硬件的性能、软件的操作系统版本,也会对性能产生影响。而这两个主要和运营商以及用户的配置有关,也是我们很难施加影响的一部分。
网络协议
今天,我将着重介绍网络协议。网络协议是一个非常重要、非常关键、但又很容易被大家忽略的一个因素。那么提到网络协议,我们会遇到或者会用到哪一些网络协议呢?
接下来,我以现在最主流的互联网下一代 HTTP2 协议为例,来简单地介绍一下。
Web 加速,协议先行!
如上图所示,WEB 请求需要经过的协议栈。该图左边是客户端,右边是数据中心,或者说服务端。
请求从客户端发出之后,首先会经过 HTTP1.1 协议。那为什么标题是 HTTP2 请求,但却提到的是 HTTP1.1?这是因为 HTTP2 虽然是一个全新的协议,但是它还是沿用了大部分 HTTP1.1 的语义。比如说 GET 请求、POST 请求,都是使用 HTTP1.1 的语义。对于页面或者对于 JavaScript 来讲,我们仍然使用着 HTTP1.1 的语义。HTTP1.1 下一层就到了 HTTP2, HTTP2 使用全新的帧控制语义,换句话说,HTTP2 frame 进行封装 HTTP1.1 的语义,比如说 GET 请求,它通过使用 HTTP2 的 HEADERS 实现封装;POST 请求的 body 数据使用 HTTP2 的 data frame 来实现封装。然后到达 TLS 协议,传输加密层,那这里为什么会有加密呢?
其主要原因是 HTTP2 RFC7540 协议规定 HTTP2 有两种实现,一种是 H2,强制使用 TLS 进行加密;另外一种是 H2C,这里的 C 是 clear, 就是明文,不需要加密。虽然协议是这么规定的,但是所有的主流实现,包括所有的浏览器、所有的操作系统、到目前为止都是使用 TLS 加密。也就是说,如果使用 HTTP2 就一定要使用 TLS 加密,所以在这过程中遇到 TLS 的问题。然后再往下是 TCP 层,再继续往下是 IP 网络层,以及以太网、运营商网络物理层,之后到达右边服务端协议上。两边端口可以认为是对称的,因此不再进行介绍。
那这么多协议,我们应该需要关注哪一些呢?或者说更明确地说,对我们大部分 WEB 应用开发者来讲,我们需要关注哪一些呢? 上图中白色虚线往上的部分是客户端可以施加影响或者优化的一部分,即协议上提出的包括 TCP、TLS 协议,以及再往上的 HTTP。明确了我们需要优化的协议,那么接下来我来分析一下他们都会有哪些问题。
HTTP1.1 的性能
HTTP1.1 的性能问题
Web 加速,协议先行!
以现在使用最广泛、历史最悠久的 HTTP1.1 协议说起。
接触 web 开发的同学都知道,HTTP1.1 最大的性能问题就是单链路串行。当一个 TCP 链接上如果有多个请求。如上图所示。请求只能一个接着一个发送,这是它最大的问题。
第二个问题是头部未压缩。头部,尤其当 HTTP 请求 Cookie、Host,其浏览器型号都一样,如果每次请求携带相同的信息,这显然是重复的。这是冗余,浪费带宽,并且从性能问题上会影响用户的访问速度。为什么呢?因为运营商的网络上下行带宽严重不对称,上行带宽很可能只有下行带宽 1/10 甚至小于 1/10,也就是说,若其带宽有十兆或者一兆,那么它上行链路可能只有 100K 甚至几十 K。如果头部平均大小是 1500 个字节,其一次传出可能有十个头部,并且在带宽层面会影响用户访问性能。因此在 3G、4G 的移动网络环境下,头部未压缩问题显得更为重要。
HTTP1.1 的优化
优化的手段、优化的策略非常多,也非常有效,那么有哪些呢?
Web 加速,协议先行!
概括来讲,HTTP1.1 的优化方向主要是两点。
第一点是增加并发连接数量;第二点是减少往外发出的请求数量。如上图,不管是单域名多 TCP 连接,还是域名分片,使用多个域名都是为了提升并发连接的数量。需要注意的是并发连接不是并发请求。因为 HTTP1.1 尝试在单链接上实现并发请求,但是失败了。比如 pipelining,因为它存在队头阻塞。所以它只能通过提升并发连接的数量来提升用户的访问性能。此外,也有其他一些优化策略,包括缓存、CSS Sprite、data uri,图片内联等。其本质都是为了减少发出请求的数量,即将多个响应放在一个响应里面返回,以此减少请求数量。
以上这些 HTTP1.1 优化策略和手段在 HTTP1.1 时代取得非常好的效果, 并且也行之有效。
HTTPS/HTTP2 加速 HTTP1.1 的淘汰
随着 HTTPS 的全站趋势越来越明显, HTTP2 正式发布渐成主流,HTTP1.1 的优化策略随之失效了。为什么这么说呢?
HTTPS 性能的一个特点或者说缺点是连接成本非常高,如果 HTTP1.1 使用增加并发连接的方式来提升性能,由于其连接成本很高,那么并发连接的并发成本也就会高,反而降低了性能。
HTTP2 的特性是支持多路复用。在一个链接上允许发出多个请求,允许并发请求。那 HTTP1.1 减少请求数量就显得有点多余,相反地,有可能会降低缓存命中率,降低性能。
所以从这个层面来讲, http1.1 的优化策略有可能行不通,并且产生副作用的。
HTTP 与 HTTPS 的比较
HTTP VS HTTPS 连接成本
接下来我简单介绍一下 HTTPS 的连接成本为什么会高?从协议层面,它的连接成本高在哪里?
Web 加速,协议先行!
如上图,左边图是一次 HTTP1.1 的请求过程或者说是成本。HTTP1.1 请求非常简单,只需要建立 TCP 连接,然后发送 HTTP 请求和响应就完成了。总过程只需要两个 RTT 就可以实现一次 HTTP 请求。
右边图是一次 HTTPS 的请求。首先 HTTPS 通过三次握手建立 TCP 连接;然后发送 HTTP1.1 请求. 这里为什么是 HTTP1.1 的数据呢?因为如果让用户主动输入 URL,大多数用户不会主动输入 HTTPS,比如说,访问腾讯网, 他们不会输入 https://www.qq.com, 而是输入 www.qq.com 或者 http://www.qq.com。为了强制用户使用 HTTPS 就会返回一个 302 跳转,关于 302 跳转,HTTPS 使用的端口是 443,HTTP 是 80;新的端口就必须重新建立一个新的 TCP 连接,这样又多了一次 TCP 连接的建立过程;建立 TCP 连接之后,开始进行 TLS 握手阶段。
TLS 握手有两个阶段,完全握手阶段一,协商协议版本、密码套件、请求证书、校验证书;客户端拿到证书之后即使证书的签名没有问题,证书时间也有效,但是仍然需要校验证书的状态,因为有可能存在主动撤销证书的情况,证书的匙钥也有可能被泄露或者说 CA 被攻击,所以查询证书状态是必要的。查询证书状态就是 OCSP,在线证书状态检查,查询证书状态需要解析个 CA 的三点 DNS,又需要建立 TCP 连接,然后又需要建立包括 OCSP 的请求响应,通过 HTTP 请求完成,三次握手、状态没问题之后,开始进行 TLS 完全握手阶段二,协商对称密钥,即非对称密钥交换计算;完成之后,整个 TLS 过程协商完毕,开始发送 HTTP 加密数据。至此达到第九个 RTT,相比 http1.1 多出 7 个 RTT,这是什么概念呢?
未经优化的 HTTPS 速度明显慢于 HTTP
Web 加速,协议先行!
如上图,现在最好的 wifi 网络环境下平均 RTT 是 70 毫秒,7 个 RTT 就是 490 毫秒,4G 是 100 毫秒,3G 是 200 毫秒, 7 个 RTT 就是一秒多钟。且这仅仅是一次请求,一个页面一般会有很多个请求,并且请求之间还可能有依赖,产生阻塞。以此情况下,一个页面多出几秒钟是非常正常的现象,且该耗时仅仅为网络耗时;而由于协议的规定,它必须要进行网络交互,还包括很多其他的耗时,比如说计算耗时,证书校验、密钥计算、内容对称的加解密等计算,由于移动端性能相对要弱,所以在这计算层面多出几百毫秒也是非常正常的现象。
经过以上分析,我们可以得出一个基本结论,就是未经优化的 HTTPS 要比 HTTP 慢很多,那么 HTTPS 是不是就是 slow,就是慢的意思呢?
为了找到慢的原因或者说找到性能的瓶颈,以上我们仅仅从网络协议理论进行分析,而事实上,在实验环境下、业务真实环境下、用户场景下面,它是不是还是这么慢,又慢在哪里呢?
为此,我们进行了两个层面的分析。
线下模拟测试
第一个就是线下模拟测试。线下模拟测试主要针对移动端,因为移动端手机性能是流量越来越多的、都是往移动端倾斜。移动端有两个特点,第一,手机屏幕比较小,查看数据或者查看页面性能,调试日志也不太方便;第二,不太方便运行数据的分析脚本、控制脚本。所以我们将手机和 PC 使用 USB 连接起来,通过远程调试协议来控制手机上的浏览器来不断的重复访问我们所构造的不同的页面。该页面所涉及的元素一定是非常多的,不同的类型也都有。
因此我们进行线下模拟测试一定要自动化,靠人为实现是没有效率的,并且也测试不了那么多的页面和场景。而且要注意数据的同比、环比,如果没有,即使测试拿到了一百条数据,一千条数据也是不能说明问题的,因为数据误差尤其多,周期性的同比、环比要求数据超过一万条才认为它可能有一定的说明意义。
另外一个大误差是 WIFI。即使是在洲际酒店、腾讯大厦,或者朗科大厦,使用的 wifi 还是经常出现网络抖动的情况,这对数据、对协议的分析有巨大影响,因为协议很可能仅仅就是那么一个 IT,那么几百毫秒的差异,在抖动的网络环境下很容易出现问题干扰结论。所以为了排除这些误差,我们也经常将手机和 PC 用 USB 连接起来,绕开 wifi 直接使用有线网络,因为有线相比无线,特别是 wifi,要稳定非常多。
关于工具的话就是 traffic control,因为我们真实用户的网络环境是千差万别的。不同 IP,不同的带宽,不同的丢包率,因此我们采用该工具来模拟在实验室里模拟环境。线下模拟数据,显然不能代表我们真实业务的性能。
线上业务速度数据采集
第二个部分是线上分析数据。
Web 加速,协议先行!
在服务端进行数据采集的方案有两个优势。
第一点它能采集到客户端采集不到的数据,采集到更低层信息和业务信息。如上图,左边是客户端,即可用手机、STW 或者是负载均衡器,右边是业务服务器。关于业务信息,通过 LB 和业务服务器的交互可以获得业务整体处理时间,这是客户端拿不到的。第二点关于协议信息,包括 TCP 的 RTT、TCP 是否连接复用、是否 TLS SESSION 复用、 TLS 协议版本,密码套件、握手时间以及非对称密钥交换计算时间,HTTP2 头部压缩比等,这些都是客户拿不到的信息。
有些信息客户端能采集到,但客户端的开发成本很高,因为安卓有不同的版本,比如有 IOS、有 windows,需要针对不同操作系统进行开发,而在服务端只需要一次开发即可,将数据放到 COOKIE 里返回给客户端,客户端通过 JS 或者普通页面就能获取到信息,拿到信息之后,将协议相关的信息组合起来放到一条数据内统一上报到数据平台进行分析。
那么拿出这么多数据,我们数据平台如何去分析呢?
多维数据分析
Web 加速,协议先行!
如上图,由于数据庞大,仅截取几条进行分析。左边是数据维度,比如 TCP-reuse 表示 TCP 连接复用,TLS1.2 表示 TLS 协议版本是 1.2;右边是与用户性能相关的监控指标,比如说开始加载时间,页面可活动时间,业务处理时间等维度,这些维度也可互相组合并得出三四百个维度。如上图所示,腾讯 X 五内核浏览器在 4G 网络下使用 HTTP2 并且是使用 TLS1.2 协议并且使用 ECDHE 并且没有复用 TLS Session 的首屏时间是多少?通过多维度对比,我们很容易就可以区别出它慢的因素, X5 在 4G 是这么多,那么在 3G 下面是多少,wifi 下面是多少呢?通过多维度综合的对比就能从表面看出瓶颈在哪里。
因此多维度数据分析,最终目的就是为了找到性能瓶颈以及速度优化方向,那么有哪些优化方向呢?
WEB 访问速度优化方向
优化方向有三个。第一个协议;第二个资源;第三个用户行为。
协议
TCP 速度优化
Web 加速,协议先行!
那么协议层面如何进行优化呢?协议的最底层是 TCP,而 TCP 最显而易见的性能问题是需要三次握手才能建立一个 TCP 连接,然后发送应用层数据,与普通握手相比,浪费一个 RTT。如上图左边是普通握手。每个 TCP 的连接需要建立握手,并且发送 SYN 包没有任何运动组数据,RTT 就浪费了。
TFO 是 TCP FAST OPEN。在 SYN 包发出的同时将应用层数据发出来,然而它的前提是第一次建立握手、建立连接的时候也需要发 SYN 包,不同的是,服务器返回 SYN+ACK 时会返回一个 COOKIE,在这之后用户发起 TCP 连接,发出 SYN 包的同时会带上这个 COOKIE,然后可以直接带上 HTTP 数据。换句话说,在 SYN 包发出的同时将应用层数据一起发出来,减少了一个 RTT 对性能的影响。TCP FAST OPEN 性能是一个收益数据,我们发现 80 分的数据提升了将近一百毫秒。然而它的缺点是需要操作系统的支持,需要 IOS9+ 或者 linux kervel3.7+,并且不支持 Windows 系统。另一个优化方案是拥塞控制,将三个拥塞窗口增加到十个。
总的来说,在 TCP 协议层面来提升速度优化的空间有限,因为优化成本非常高,而高在哪里呢?它需要操作系统、需要内核的支持,如果仅仅是服务端升级,我们支持与研发,其成本还能够控制,但是最重要的是需要用户的操作系统升级,需要广大网络设备上的软协议栈或者操作系统升级,而在这个层面部署的压力非常大。所以 TCP 层面优化成本非常高。
TLS 速度优化 -session reuse
TLS 是加密层,加密层最大的问题是完全握手。关于完全握手,接触过的同学应该很清楚它的 session ID,它的过程我就不做介绍了。
Web 加速,协议先行!
在这里,我主要分享两点。如上图。第一点,关于 IOS Qzone 优化,通过 session id 握手时间大概能够提升 50%;第二点,session ticket 的机制虽然更加先进,因为它不需要服务端提供缓存去提供内存存储,发送 ticket,服务端进行校验即可。而 session ID 需要提供内存进行存储,然后查找到 session cache 是否命中,由于 IOS 不支持 session ticket,所以大家一定要注意通过分布式 session cache 来提升 IOS 的 session ID 的缓存命中率。很多场景是没有办法实现简化握手的,比如说用户第一次打开 APP、打开浏览器,又比如说用户退出了再重新重启浏览器,或者说把浏览器页面关闭掉,再打开,都可能会导致 session cache 简化握手无法实现,因为 session ticket 都是基于内存的,而不是存储在硬盘里面。
TLS 速度优化——False Start
Web 加速,协议先行!
针对完全握手的优化方法是 false start,即抢跑。与 TFO 思路类似,如上图左边是普通握手,必须要进行 TLS 的两个 RTT 四次握手才能建立连接,发送应用层数据。false start 是在 clientKeyExchange 没有交换的时候,即在完全握手的第二个阶段将应用层数据一起发出来,提升了一个 RTT。那怎么启用 false start 呢?其实现在客户端基本都已经支持了,如果服务端要启用需要注意将支持 PFS(完美前向密码)加密的算法配置在前,比如说 ECDHE、DHE 算法配置在前即可,并且其握手时间将提升 30%。
TLS 速度优化——OCSP Stapling
OCSP Stapling。刚才提到的一些场景就是主动撤销证书,所以一定要进行证书状态的检查,因为在某些情况下,单纯地进行校验证书的签名、校验证书的时间是发现不了证书的状态,它需要更加实时的检查。我们证书颁布 3 年,学校中间可能出问题,所以他会进行周期性的检查。
Web 加速,协议先行!
他普通的过程与之类似, client hello 获取到证书时,③④⑤这过程中增加的 3 个 RTT 需要去 CA 站点请求 OCSP 的状态和内容。如上图,右边是 OCSP Stapling,它的过程相当于服务器代理实现了 CA 的证书状态颁发的功能, 它提前向 CA 站点请求 OCSP 内容,并保存在本地服务器端,然后在握手的同时,将像签名的内容返回给客户端,然后客户端对该内容进行校验。它不需要直接和 CA 站点进行交互,因此这样看来 OCSP Stapling 减少了三个 RTT,效果应该非常明显。
但是,事实上不是这样的,为什么?因为客户端会对 OCSP 进行缓存,没请求一次就可能可以缓存七天,也就是在这七天时期,如果用户访问该网站次数非常多,可能成千上万次,因为一个页面有非常多的请求,所以可能只有 1‰的概率来增加这三个 RTT。大家需要注意的,并不是一定就有这个效果。
TLS 速度优化——dynamic record size
Web 加速,协议先行!
现在来分析一下 dynamic record size,即记录块大小的动态的调整。
先简单讲解一下 TLS 协议层面的队头阻塞,即 head of line blocking 问题产生的背景,因为 TLS 协议传输的最基本单位是 record,一个记录块最大不能超过 16K,而一些服务端的实现,比如说 Nginx 最早期版本默认大小 16K。那么 16K 会产生什么情况呢?如果你中间丢了一个字节,或者说由于 TCP 的筹码丢了一个 segment,将导致这 16K 没有办法被校验,就算接收到 15.99K 的数据,也没有办法处理,因为一个数据的丢失、一个包的丢失,导致整个 record 没有办法被处理。
那怎么解决呢?方法有两个。
第一个就是适当的调小 buffer,比如说改成 4K,不用实名制,即使丢失了也只影响这 4k 数据,而不是 16K 数据。
第二个,借鉴 TCP 的 slow start 原理。cloud flare 提供一个 patch,在 TLS 连接刚刚建立的时候,由于不知道网络状况以及拥塞情况,将 record 设置缩小,比如变成 1K, 在发送速度提升之后,再将 record size 设置为 16K。它大概是这么个思路,也有开源代码,大家有兴趣的话可以了解一下。
刚才提到这些优化策略都是针对 TLS1.2 以及之前的版本。接下去介绍 TLS1.3 版本,这是一个革命性的、非常创新的、具有里程碑意义的协议。
TLS1.3 速度优化
TLS1.3 速度优化——ORTT Handshake
在性能方面的创新点是它能实现 1RTT 的完全握手,以及实现 0RTT 的简化握手。也就是说,TLS 连接层面没有握手原则不会增加 RTT 的延时。TLS1.3 现在仍处于草稿阶段,这里介绍它,是因为如果大家想尝鲜已经可以使用了,因为 OpenSSL1.1.1,Nginx1.13.0 已经分别支持 TLS1.3 的最新的草稿 draft20。而且 TLS1.3 上的最新的讨论,可能将于今年秋季正式发布。如果客户端是自己可以控制的,那可以尝试,包括 Firefox 也支持 TLS1.3。当然这些都是草稿版本,所以这里给大家介绍一下,具体的过程其实也比较复杂,我这里就不做详细介绍了。
HTTPS 速度优化
HTTPS 速度优化——HSTS 减少 302 跳转
HSTS 其实是 HTTP 的头部,它的作用是服务端告诉客户端,你以后访问我的网站,在指定的时间内,你必须使用 HTTPS,你不要使用 HTTP。然而他存在一个问题是 HSTS 是通过 HTTP 返回的,很有可能被中间者所劫持。也就是说,客户端可能永远不知道服务端曾经发送 HSTS,也就没有办法使用 https。这样的话,有一个薄弱的预加载名单机制,比如说 chrome 浏览器将把你的网站内置在白名单里,当你发出访问请求时,它会默认使用 HTTPS;客户端同理。
HTTPS 速度优化——SPDY&&HTTP2
SPDY 和 HTTP2。为什么会提到 SPDY?主要有两点。
第一点,因为现在 HTTP2 很流行,并且正式发布了,但是它的所有特性和最主要特性都是沿用于 SPDY,除了头部压缩算法,所以我最开始研究的也是 SPDY 的协议,而且现在可能很多人只知道 HTTP2 而不知道 SPDY。所以也是为了向该协议致敬,因为它是鼻祖。
第二点,大多数老客户端只支持 SPDY,比如说安卓 4.4 以前版本,以及 IOS 现在还支持 SPDY,为了兼容老版本,提升他们性能同时支持 SPDY 和 HTTP2。
它有三个特性。
第一个,多路复用。在单个链接上同时发送多个请求,并且请求和请求之间在协议层面可以不依赖也可以依赖。比如说第二个请求先处理完了可以提前返回,相比较 HTTP1.1,它也做过 pipelining 的努力,它允许客户端同时发出多个请求,但是服务器必须按照严格顺序返回,否则就会乱套。它不知道哪个请求和哪个响应对应的。这样会导致即使第三个请求先处理完了也不能提前返回;或者说四个请求都处理完,但是第三个请求丢了,那整个顺序也就出问题了。HTTP2 最强大的特性就是多路复用,它能将一百个请求甚至设置一百多个请求在一起发出去。
关于 SPDY 和 HTTP2,大家可能容易忽略的两点。第一点头部压缩,压缩的数据、宣传的页面大概会有 89% 或者 90% 的压缩比,但事实上是这样的吗?通过测试发现在一个 TCP 连接发送第一个请求的时候,当时的 HTTP2 压缩比只有 30%,发生第二个请求的时候压缩比能达到 60%,发生第三个请求以及之后才可能达到 90%,因为 SPDY 使用的是基于 zlib 压缩算法,而 HTTP2 使用的是基于 Hpack 压缩算法,他们都是基于状态空间进行压缩,因此如果信息越冗余、越重复,当你在这个连接上重复性阅读,压缩比才会越高。所以第一个请求发生时,头部没有那么冗余,压缩比可能就只有 30%。
这给我们性能优化方面的一个启发就是如果一个页面要放到下一个页面,我们可以提前给页面或者域名的连接发送两个空请求,积累数据。当真实的用户请求发起的时候已经达到了第三个合作之后了,以至于用户的头部压缩比就能达到 90%。
第二个是 server push。由于 Nginx 不支持 server push,所以现在这个应用在国内用得还很少,但它作用确实很大。比如说客户端请求一个 html, html 里面含有 css 和图片,按照正常来讲,解析 html 之后要分别发出 CSS 的请求和图片的请求,但是如果服务端得知页面支持 server push,客户端便只需要发出 http 请求,而服务器直接将 css 和图片一起发出去,以致请求多个响应未发先至。这就是 server push 的作用,和 inlining 有点类似,但是相比 inlining 有两个好处,inlining 会影响缓存,会增大 html 的体积,包括后台模板的维护,这也便增加开发和维护成本,对于客户而言仅仅是多个请求。
HTTP2 实践建议
关于 HTTP2P 的实践建议以及原理性的知识,在网上资料已经非常多了,所以我主要是告诉大家一些我们自己在实践中遇到的一些经验和心得。
第一,使用一个连接或者说使用更少的链接。不用使用很多连接,为什么?第一,连接少,握手成本就少。第二,压缩比高,因为一个连接上积累的信息很多,压缩比会更高。第三,更好地利用 TCP 的特性,为什么?因为 TCP 的拥塞控制流量控制都是基于单个连接的,如果使用很多个连接,特别是在网络拥塞的情况下,会放大拥塞的系数,加剧网络拥塞,如果使用一个连接,当得知该窗口已经拥塞,响应很慢便不会继续发送了,但是多个连接的情况,可能大家都会尝试发一下,而导致加剧网络拥塞。
第二,使用更少的域名,这也是为了更好地使用连接,并且减少了 DNS 解析时间。
第三,如果一定要使用很多域名,那就尽量将多个域名解析到相同的 ip,使用相同的证书。因为客户端实现的角度标准就是如果多个域名能够复用相同的 ip 和相同的证书,那我就会复用这个连接,我不会新建连接。比如 chrome 浏览器;灵活运用 server push,代替 inlining;关于 TLS1.2, 若其默认使用 TLS1.2 之前的版本,HTPP2 便没有办法使用。TLS1.2 并不是支持所有的密码套件,它有很多密码套件属于黑名单,这也是在 HTPP2 现在的使用率比较低的一个原因;HTPP2 并不能解决所有问题,它适用于多元素、多连接的场景,如果你的页面本身很简单,只有几个请求,就用普通 TLS 也没问题,它并不能提升该场景下的访问性能。但是总的来说肯定还是推荐大家使用。
用户行为
预建连接
预建连接是一个最简单、最有效的速度优化的方案,为什么?预建连接的思路是在用户发起真正的连接之前,提前建立好连接。这样的话,对于用户而言,由连接所导致的成本和延时是感觉不到的。之前的一系列优化方案,即使是 0 RTT 握手,也存在一些计算,比如 ticket 的校验。
那怎么样来预建连接呢?共有两点方法。
第一点通过 link 头部标签,比如说连接告诉浏览器,即服务端,可以返回;或者告诉浏览器,页面可能需要访问下一个页面,那提前跟我建立好预连接。这是支持 link 头部的浏览器可以实现的。如果不支持该特性,那怎么办呢?
第二点通过控制页面 JS。比如说,后台页面上有个 JS,对于即将访问的页面,可以提前与它建立好连接。当用户发起请求时,JS 已经提前给它预建连接了。还有很多场景,第一个是首页可以提前预建子页面连接,比如说百度首页,百度首页很简单,它的搜索结果千差万别,但是搜索结果的网站也可以固定好 AA.com,这样便于建立连接;第二个 QQ 空间,有的用户打开 QQ 空间可能会相册,有些用户会经常访问商城挂件、商城访问礼物,那我们就可以根据用户经常访问的页面给它提前建立好不同的直接预连接,预建好的连接也有可能会超时断开。
那么如何避免呢?可以使用长连接保持。比如说浏览器 HTTP2 超过三分钟就会断掉,HTTP 超过五分钟就会断掉,为了维持住长连接,我们可以使用 JS 周期性给这些页面发起长连接保持的请求,这样的话,连接相当于一直会保持住用户在访问时候建立好的状态,这便是预建连接的一个好处。
Web 加速,协议先行!
关于 HTTPS 速度的一个基本结论是 HTTPS 的访问速度可以超越 HTTP1.1。其最关键、最核心的一点是 HTTPS 可以使用多路复用,而 HTTP1.1 不行。关于优化手段节点,如上图,这是两年前的数据,它们的优化策略是一样的,包括 HTTP2、SPDY 都没有本质区别。
HTTP2 是未来吗?
Web 加速,协议先行!
之前提到 HTTP2 的一些强特性,对我们非常有帮助。如今 HTTP2 被认为是互联网下一代协议,但是它真的是我们未来吗?或者说,HTTP2 是下一个十年最具有性能权威、最具有统治力的一个协议吗?回答是肯定的。是。因为它的很多特性,比如多路复用、头部压缩、server push、优先级等,设计非常先进,性能也及其优良,解决了很多性能问题。
但回答也可以说不是。为什么?因为现在的 HTTP2 是基于 TCP 协议和 TLS 协议而构建,存在着些问题。
首先 TCP 协议 3 次握手。虽然它可以支持 TFO,但是 TFO 需要操作系统支持。并且 TFO 第一次建立连接获取 COOKIE 时还需要一次额外的 RTT 才能实现握手。此外 TLS1.3 支持 0RTT 握手,但 TLS 对 TCP 头部没有进行验证,可能存在被篡改的风险,比如修改窗口数、修改序列号等,都存在被劫持的可能。
而最大的问题是 TCP 的队头阻塞,比如说当我们传输 segment 时,如果第二个 segment 丢了,那便传输失败,为了保证它的可靠性和有序性,需要等到第二个 segment 到达才能往上传给应用层,而糟糕的是,由于 HTTP2 多路复用的特性,如果将假设的 50 个请求在一个 TCP 平台上发送,它会加剧队头阻塞的问题。关于 TCP 的重传,重传的序列号和最原始的序列号是相同一个序列号,这会导致重传的模糊性问题。关于拥塞控制,需要操作系统升级,但是 TCP 的升级成本高,需要用户、网络设备中间商去升级来支持。
所以从协议层面来讲,HTTP2 不是下一个十年最具有统治力或者最有心的、有权威的一个协议,那什么才是非常具有竞争力的一个协议呢?它就是 QUIC 协议,即使用 UDP 实现 HTTP2。以下是它的特性。
拥抱 QUIC 协议
Web 加速,协议先行!
它支持 HTTP2 的所有特性,比如多路复用、队头阻塞、头部压缩、server push;支持使用 TLS 1.3 的 0RTT 握手;使用 UDP 传输使用;基于 packet 加密,对 packet 头部进行一致性的验证,一旦发生修改即可发现。
以上我所提及到的协议优化,腾讯云也都进行了很好的支持。另外,我们腾讯云 CLB 也正式支持了 QUIC 协议,平均性能提升了 15%以上,欢迎大家试用。
作者介绍
Web 加速,协议先行!
罗成,2011 年毕业于浙江大学,2011 年至 2015 年任职于百度运维部,主要负责文件分发、统一接入、安全搜索等事项,2015 年开始任职于腾讯,腾讯 TEG 基础架构部,高级工程师,目前主要负责腾讯 STGW(腾讯安全云网关)的相关工作。对 HTTPS,SPDY,HTTP2,QUIC 等应用层协议、高性能服务器技术、云网络技术、用户访问速度、分布式文件传输等有较深的理解。
福德麟管理咨询福德麟管理咨询http://www.qdfoodlin.com