Web网页加速需要注意协议存在的问题
时间:2020-05-17 13:02来源:无锡做网站公司
问 Web 网站和应用时,经常会遇到各种各样的性能问题。比如网页加载慢、视频卡、网络出错等,其中一个关键的影响因素就是网络协议。本文会系统化地介绍 TCP、UDP、HTTP1.1、HTTPS(包括最新的 TLS1.3 协议)、SPDY、HTTP2 等协议存在的问题,以及如何在特定的场景下通过网络协议的优化实现访问速度的提升。
注:本文整理自腾讯 TEG 基础架构部高级工程师罗成在 ArchSummit 2017 深圳站上的演讲。
前言:WEB 性能优化的问题
提到 WEB 性能优化,那也就意味着我们遇到了很多性能方面的问题。
如上图所示,看到这一行字,我相信大家的脑海里一定会浮现出很多的场景画面,比如说无法接入网络,又比如说页面一直在加载,或者说内容可能一直卡在 99% 加载不出来,包括视频的卡顿、短视频以及直播,还有从 wifi 切到 4G 网络出现重新加载的情况,那遇到这么多的 web 问题,其主要原因是什么呢?
我进行简单罗列一下,归纳为有以下两种情况。
直接和页面相关的页面大小,页面的元素类型的多少,也就是说一个页面越大,元素越多,动态的交互越频繁,那相应的性能可能就会受到更大影响。页面优化问题也是前端优化的主战场。
网络环境以及端配置,包括用户手机硬件的性能、软件的操作系统版本,也会对性能产生影响。而这两个主要和运营商以及用户的配置有关,也是我们很难施加影响的一部分。
网络协议
今天,我将着重介绍网络协议。网络协议是一个非常重要、非常关键、但又很容易被大家忽略的一个因素。那么提到网络协议,我们会遇到或者会用到哪一些网络协议呢?
接下来,我以现在最主流的互联网下一代 HTTP2 协议为例,来简单地介绍一下。
如上图所示,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 的性能问题
以现在使用最广泛、历史最悠久的 HTTP1.1 协议说起。
接触 web 开发的同学都知道,HTTP1.1 最大的性能问题就是单链路串行。当一个 TCP 链接上如果有多个请求。如上图所示。请求只能一个接着一个发送,这是它最大的问题。
第二个问题是头部未压缩。头部,尤其当 HTTP 请求 Cookie、Host,其浏览器型号都一样,如果每次请求携带相同的信息,这显然是重复的。这是冗余,浪费带宽,并且从性能问题上会影响用户的访问速度。为什么呢?因为运营商的网络上下行带宽严重不对称,上行带宽很可能只有下行带宽 1/10 甚至小于 1/10,也就是说,若其带宽有十兆或者一兆,那么它上行链路可能只有 100K 甚至几十 K。如果头部平均大小是 1500 个字节,其一次传出可能有十个头部,并且在带宽层面会影响用户访问性能。因此在 3G、4G 的移动网络环境下,头部未压缩问题显得更为重要。
HTTP1.1 的优化
优化的手段、优化的策略非常多,也非常有效,那么有哪些呢?
概括来讲,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 的连接成本为什么会高?从协议层面,它的连接成本高在哪里?
如上图,左边图是一次 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
如上图,现在最好的 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,不同的带宽,不同的丢包率,因此我们采用该工具来模拟在实验室里模拟环境。线下模拟数据,显然不能代表我们真实业务的性能。
线上业务速度数据采集
第二个部分是线上分析数据。
在服务端进行数据采集的方案有两个优势。
第一点它能采集到客户端采集不到的数据,采集到更低层信息和业务信息。如上图,左边是客户端,即可用手机、STW 或者是负载均衡器,右边是业务服务器。关于业务信息,通过 LB 和业务服务器的交互可以获得业务整体处理时间,这是客户端拿不到的。第二点关于协议信息,包括 TCP 的 RTT、TCP 是否连接复用、是否 TLS SESSION 复用、 TLS 协议版本,密码套件、握手时间以及非对称密钥交换计算时间,HTTP2 头部压缩比等,这些都是客户拿不到的信息。
有些信息客户端能采集到,但客户端的开发成本很高,因为安卓有不同的版本,比如有 IOS、有 windows,需要针对不同操作系统进行开发,而在服务端只需要一次开发即可,将数据放到 COOKIE 里返回给客户端,客户端通过 JS 或者普通页面就能获取到信息,拿到信息之后,将协议相关的信息组合起来放到一条数据内统一上报到数据平台进行分析。
那么拿出这么多数据,我们数据平台如何去分析呢?
多维数据分析
如上图,由于数据庞大,仅截取几条进行分析。左边是数据维度,比如 TCP-reuse 表示 TCP 连接复用,TLS1.2 表示 TLS 协议版本是 1.2;右边是与用户性能相关的监控指标,比如说开始加载时间,页面可活动时间,业务处理时间等维度,这些维度也可互相组合并得出三四百个维度。如上图所示,腾讯 X 五内核浏览器在 4G 网络下使用 HTTP2 并且是使用 TLS1.2 协议并且使用 ECDHE 并且没有复用 TLS Session 的首屏时间是多少?通过多维度对比,我们很容易就可以区别出它慢的因素, X5 在 4G 是这么多,那么在 3G 下面是多少,wifi 下面是多少呢?通过多维度综合的对比就能从表面看出瓶颈在哪里。
因此多维度数据分析,最终目的就是为了找到性能瓶颈以及速度优化方向,那么有哪些优化方向呢?
WEB 访问速度优化方向
优化方向有三个。第一个协议;第二个资源;第三个用户行为。
协议
TCP 速度优化
那么协议层面如何进行优化呢?协议的最底层是 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,它的过程我就不做介绍了。
在这里,我主要分享两点。如上图。第一点,关于 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
针对完全握手的优化方法是 false start,即抢跑。与 TFO 思路类似,如上图左边是普通握手,必须要进行 TLS 的两个 RTT 四次握手才能建立连接,发送应用层数据。false start 是在 clientKeyExchange 没有交换的时候,即在完全握手的第二个阶段将应用层数据一起发出来,提升了一个 RTT。那怎么启用 false start 呢?其实现在客户端基本都已经支持了,如果服务端要启用需要注意将支持 PFS(完美前向密码)加密的算法配置在前,比如说 ECDHE、DHE 算法配置在前即可,并且其握手时间将提升 30%。
TLS 速度优化——OCSP Stapling
OCSP Stapling。刚才提到的一些场景就是主动撤销证书,所以一定要进行证书状态的检查,因为在某些情况下,单纯地进行校验证书的签名、校验证书的时间是发现不了证书的状态,它需要更加实时的检查。我们证书颁布 3 年,学校中间可能出问题,所以他会进行周期性的检查。
他普通的过程与之类似, client hello 获取到证书时,③④⑤这过程中增加的 3 个 RTT 需要去 CA 站点请求 OCSP 的状态和内容。如上图,右边是 OCSP Stapling,它的过程相当于服务器代理实现了 CA 的证书状态颁发的功能, 它提前向 CA 站点请求 OCSP 内容,并保存在本地服务器端,然后在握手的同时,将像签名的内容返回给客户端,然后客户端对该内容进行校验。它不需要直接和 CA 站点进行交互,因此这样看来 OCSP Stapling 减少了三个 RTT,效果应该非常明显。
但是,事实上不是这样的,为什么?因为客户端会对 OCSP 进行缓存,没请求一次就可能可以缓存七天,也就是在这七天时期,如果用户访问该网站次数非常多,可能成千上万次,因为一个页面有非常多的请求,所以可能只有 1‰的概率来增加这三个 RTT。大家需要注意的,并不是一定就有这个效果。
TLS 速度优化——dynamic record size
现在来分析一下 dynamic record size,即记录块大小的动态的调整。
先简单讲解一下 TLS 协议层面的队头阻塞,即 head of line blocking 问题产生的背景,因为 TLS 协议传输的最基本单位是 record,一个记录块最大不能超过 16K,而一些服务端的实现,比如说 Nginx 最早期版本默认大小 16K。那么 16K 会产生什么情况呢?如果你中间丢了一个字节,或者说由于 TCP 的筹码丢了一个 segment,将导致这 16K 没有办法被校验,就算接收到 15.99K 的数据,也没有办法处理,因为一个数据的丢失、一个包的丢失,导致整个 record 没有办法被处理。
声明:除非注明,本站内容由无锡网站建设马氪软件原创发布©,转载请联系我们授权合作。