从HTTP/2到TLS 1.3:升级SSL之后你还能顺带给网站提速的三个隐藏功能
一个上线好几年但从没正经优化过的老网站,页面上同时挤了几十个JS和CSS文件,用的是HTTP/1.1协议,SSL证书虽然装了但也就图个绿锁,没管过协议版本。每次打开这个网站,浏览器地址栏右下角那个加载圈圈都要转很久,Chrome开发者工具里的Timeline一看,Stalled那一块占了将近两秒。我当时以为是带宽不够或者服务器CPU太弱鸡,后来才意识到,原来我一直把HTTPS当作“安全保护费”——觉得既然要加密就得牺牲点速度,天经地义。但事实上,从HTTP/1.1升级到HTTP/2,再把TLS版本从1.2提到1.3,在某些场景下页面加载速度不仅不会变慢,反而能比明文HTTP还要快。这篇就来聊聊,升级完SSL之后,顺手帮你白捡的三个提速功能。
第一个隐藏功能:多路复用,让你的请求塞车变高速
要理解HTTP/2为什么让网站变快,你得先看清楚HTTP/1.1是怎么让人难受的。HTTP/1.1的请求是串行的——这个请求回应了,下一个请求才发得出去。访问一个有八十多个资源的网站、浏览器一个个来,桌面端你要等多久?为了弥补这种低效率,浏览器会对每个域名同时打开六到八个TCP连接来做并发请求。听起来挺好?但这些连接本身有开销——六条连接,每条都得做一次TCP握手、TLS握手,每一条都消耗客户端和服务端的资源。而且它解决不了根本问题:如果某个连接上的第一个请求被数据库慢查询卡了三秒,挂在这个连接后面的所有请求都得等着,一点办法都没有。这就是所谓的队头阻塞问题。
HTTP/2是怎么解决的呢?它的核心是多路复用——在单个TCP连接上建立了无数个双向“流”,每个流独立处理一个请求的所有响应,这些流可以交错发送、互不干扰。用大白话来讲就是:以前你开车去三个地方接人,每次只送一个,必须跑三趟。现在换了辆大面包车,把三个人都接上再一起出发,一次性全送到。结果就是你不再需要等前面的请求完成,所有资源几乎是同时开始加载的。阿里云开发者社区的一个技术实测中有一组具体数据:相比HTTP/1.1,HTTP/2的首屏渲染时间能够缩短百分之三十到百分之五十。另外有一套来自国外性能测试网站DebugBear的数据同样佐证了这个结论——HTTP/2允许请求并发执行,因此响应等待时间更短,总体加载速度最快-。而且因为只维护一条TCP连接,服务器和客户端都省下了大量原本用来管理多条连接的开销,这在高频AP I交互的场景里尤其明显。访问你网站的那些游客,他们可能用的是手机端的弱网络环境,甚至是在地铁上、高铁上信号忽好忽坏,这时候多路复用的通道聚合能力能让页面更快完成首次渲染。
第二个隐藏功能:HPACK头部压缩,那些看不见的大象消失了
很多从HTTP/1.1时代转型的开发者,对头部开销这个问题几乎完全没概念。你可能在Chrome开发者工具里看到过一串长长的请求头,里面包含了各种Cookie、User-Agent、Accept、Referer之类的字段。这些头部信息在每一个请求里都会重复传递,而且绝大多数都是相同的冗余数据。一个小网站,每次访问页面,光是把这些重复的头部数据来回传一遍,每个月就能浪费掉几十个G的带宽。更麻烦的是在网络条件差的时候,这些多余的字节会拖慢整个页面的首字节时间。HTTP/2引入了一种叫HPACK的压缩算法——在客户端和服务器端各自维护一份静态表和动态表,两头都记住了常见的头部字段和对应的索引值,每次传输只发送索引号。发送端还用上了霍夫曼编码,给高频出现的字符分配更短的编码,进一步缩短了数据传输长度。按照上游的实际测试数据,HTTP/2的头部压缩相对于普通未压缩文本形式,可以做到减少百分之七十到百分之八十五的传输数据量。你想想看,你原本每个请求发送的头部体积大约五百到八百字节,现在变成了几十甚至十几个字节,省下来的带宽可以用来传输真正的页面内容。而且头部体积小了,网速需求自然就降下来了。这项技术一个经常被低估的收益是:它在同一条TCP连接的所有流之间共享一个压缩上下文,随着时间推移,动态表里积累的头部模式越来越丰富,压缩效率还会持续提高。
第三个隐藏功能:TLS 1.3的极简握手与0-RTT快速重连
关于加密拖慢网站这件事,我听过太多抱怨了。这种说法在TLS 1.2时代确实不冤——互联网上两个端点建立一条HTTPS连接,TLS握手需要经过两次完整的往返通信才能完成(术语上叫2-RTT),再加上底层的TCP三次握手,一个用户访问你的网站,光是建立安全连接就得等好几轮延迟。在大陆用户访问香港服务器这种典型的高延迟跨地区网络环境中,单次RTT可能达到一百五十到两百毫秒甚至更高,叠加起来,三百到四百毫秒就凭空浪费掉了。而且这还是首次访问的情况——你用了一堆CDN、做好了所有缓存优化,结果用户还没看到你网站的任何内容,就扔了半秒钟在等握手完成。
TLS 1.3对这一流程做了很大力度的重构。它把原来TLS 1.2中需要两趟往返才能完成的秘钥协商精简到了一趟往返可以完成,也就是从2-RTT压缩到了1-RTT,直接把首次连接的延迟缩减了百分之五十以上。单个用户的加载时间从三百毫秒降到了一百五十毫秒以内,而且这个下降对每个第一次访问你的游客有效。但你可能会问:那回访的用户呢?TLS 1.3提供了0-RTT就能快速重连的会话恢复机制——首次握手完成后,服务器生成一个会话票据推送给客户端,客户端把它缓存起来。当同样一个用户在几小时后重新访问你的网站时,这个刚刚用过的安全通道可以被快速复用。客户端直接在握手的时候就把加密数据发出去了,不需要等待任何一次完整握手结束。从eMudhra的一份研究报告来看,这对高频AP I调用和移动端应用特别友好,因为重新建立加密连接的延迟几乎降到了零。你可能会问,0-RTT能不能保证万无一失?它有潜在的重放攻击风险,但主流实现的防护措施做得相当成熟,通常配合企业级AP I网关和使用限制策略就能规避。实际上,对于普通的个人博客和中小型业务网站来说,你最大的烦恼往往不是0-RTT的安全微风险,而是你的服务器配置压根儿没开TLS 1.3,导致每个回访用户还得老老实实重复一次昂贵的完整握手。除此之外,TLS 1.3还淘汰了RC4、3DES等一堆老旧的弱加密算法,只保留AES-GCM这种认证加密方式,让服务器的加密计算量至少下降了百分之二十到三十,用更少的CPU周期完成同样的加密任务。
藏在角落里的第四重彩蛋:更少的加密参数协商
前面的三个功能已经是提速的主要战场,但还有一个在底层默默起作用的优化常常被忽略——TLS 1.3大幅压缩了握手过程中需要协商的参数数量,从TLS 1.2时期数量众多、配置繁琐的密码套件,简化到了寥寥几个必须的安全选项。在TLS 1.2中,服务器要在成百上千种密码组合中选出双方都支持的那个,这个过程既消耗时间也增加出错的风险。TLS 1.3直接废弃了不安全或者过时的交换方式和加密组合,只保留了现代的经过认证的加密算法。握手消息本身也变短了,因为客户端在第一条消息里就完成了所有必要的密钥共享给服务器,减少了后续的消息交换轮次。这个变化不像0-RTT那么显眼,但对于高并发系统来说,每减少几个字节和一次交叉协商,总体吞吐量就能发生肉眼可见的提升。尤其对2核4G这种中小规模的香港云服务器而言,CPU负担一旦降下来,你的业务承载能力无形中就上了一个台阶。
值得警惕的过度优化
既然HTTP/2和TLS 1.3带来这么多好处,那你是不是应该把所有传统优化方案都扔掉然后彻底依赖这些协议?千万别这么干。文件合并和雪碧图在HTTP/1.1时代是救星,进入HTTP/2以后,多个小请求不再是性能瓶颈,于是有些开发者反过来把合并过的文件拆成小块走多路复用加载,这种做法本身是没错的。但过分依赖协议优化有时适得其反。例如,动态表占用的内存上限在一些高并发服务器上可能成为新瓶颈,如果http2_max_concurrent_streams设置得过高或者单机连接数过大,HPACK动态表会消耗可观的内存资源。
同样地,0-RTT虽然在二次访问时极快,但它的重放攻击风险在高频交易的某些场景中必须搭配额外的防御措施,你不能轻轻松松打开开关就以为世界太平了。优化这件事推进每一步之前都得想清楚对手的可能反应。
CN
EN