8.0 KiB
目录
- 前言
- 在客户端缓存资源
- 压缩传输的数据
- 消除不必要的请求字节
- 并行处理请求和响应
- 针对 HTTP/1.1 的优化建议
- 针对 HTTP/2.0 的优化建议
前言
无论什么网络,也不管所用网络协议是什么版本,所有应用都应该致力于消除或减少不必要的网络延迟,将需要传输的数据压缩至最少。这两条标准是经典性能优化最佳实践,是其他数十条性能准则的出发点:
-
减少 DNS 查找
每一次主机名解析都需要一次网络往返,从而增加请求的延迟时间,同时还会阻塞后续请求。
-
重用 TCP 连接
尽可能使用持久连接,以消除 TCP 握手和慢启动延迟。
-
减少 HTTP 重定向
HTTP 重定向极费时间,特别是不同域名之间的重定向,更加费时;这里面既有额外的 DNS 查询、TCP 握手,还有其他延迟。最佳的重定向次数为零。
-
使用 CDN(内容分发网络)
把数据放到离用户地理位置更近的地方,可以显著减少每次 TCP 连接的网络延迟,增大吞吐量。
-
去掉不必要的资源
-
在客户端缓存资源
应该缓存应用资源,从而避免每次请求都发送相同的内容。
-
传输压缩过的内容
传输前应该压缩应用资源,把要传输的字节减至最少。
-
消除不必要的请求开销
减少请求的 HTTP 首部数据(比如 HTTP Cookie),节省的时间相当于几次往返的延迟时间。
-
并行处理请求和响应
请求和响应的排队都会导致延迟,无论是客户端还是服务端。
-
针对协议版本采取优化措施
HTTP/1.x 支持有限的并行机制,要求打包资源、跨域分散资源,等等。相对而言,HTTP/2.0 只要建立一个连接就能实现最优性能,同时无需针对 HTTP/1.x 的那些优化方法。
在客户端缓存资源
要说最快的网络请求,那就是不用发送请求就能获取资源。将之前下载过的数据缓存并维护好,就可以做到这一点。对于通过 HTTP 传输的资源,要保证首部包含适当的缓存字段:
- Cache-Control 首部用于制定缓存时间
- Last-Modified 和 ETag 首部提供验证机制
只要可能,就给每种资源都指定一个明确的缓存时间。这样客户端就可以直接使用本地副本,而不必每次都请求相同的内容。类似的,指定验证机制可以让客户端检查过期的资源是否有更新,没有更新,就没必要重新发送。
最后,还要注意应同时指定缓存时间和验证方法。
压缩传输的数据
利用本地缓存可以让客户端避免每次请求都重复取得数据。不过,还是有一些资源是必须取得的,比如原来的资源过期了,或者有新资源,再或者资源不能缓存。对于这些资源,应该保证传输的字节数最少。因此要保证对它们进行最有效的压缩。
HTTP、CSS 和 JavaScript 等文本资源的大小经过 gzip 压缩平均可以减少 60~80%。而图片则需要仔细考量,比如选择最优的图片格式。
这里说一下 WebP 格式,它是 Google 开发的一种新的图片格式,这种格式的无损压缩和有损压缩效能都有所提升:
- WebP 的无损压缩图片比 PNG 的小 26%
- WebP 的有损压缩图片比 JPG 的小 25%~34%
- WebP 支持无损透明压缩,但因此仅增加了 22% 的字节
但是,这种格式需要客户端 CPU 多花点时间解码(大约相当于处理 JPG 的 1.4 倍),但字节的节省完全可以补偿处理时间的增长。
消除不必要的请求字节
HTTP 是一种无状态协议,也就是说服务器不必保存每次请求的客户端的信息。然而,很多应用又依赖于状态信息以实现会话管理、个性化、分析等功能。为了实现这些功能,HTTP 做了扩展,允许任何网站针对自身来源关联和更新 Cookie 元数据:浏览器保存数据,而在随后发送给来源的每一个请求的 Cookie 首部中自动附加这些信息。
上述标准并未规定 Cookie 最大不能超过多大,但实践中大多数浏览器都将其限制为 4KB。与此同时,该标准还规定每个站点针对其来源可以有多个关联的 Cookie。于是,一个来源的 Cookie 就有可能多达几十 KB。不用说,这么多元数据随请求传递,必然会给应用带来明显的性能损失:
- 浏览器会在每个请求中自动附加关联的 Cookie 数据
- 在 HTTP/1.x 中,包括 Cookie 在内的所有 HTTP 首部都会在不压缩的状态下传输
- 在 HTTP/2.0 中,这些元数据经过压缩了,但开销依然不小
- 最坏的情况下,过大的 HTTP Cookie 会超过初始的 TCP 拥塞窗口,从而导致多余的网络往返
应该认真对待和监控 Cookie 的大小,确保只传输最低数量的元数据,比如安全会话令牌。同时,还应该利用服务器上共享的会话缓存,从中查询缓存的元数据。更好的结果,则是完全不用 Cookie,比如在请求图片、脚本和样式表等静态资源时,浏览器绝大多数情况下不必传输特定于客户端的元数据。
并行处理请求和响应
为了让应用响应速度达到最快,应该尽可能第一时间就分派所有资源请求。可是,还有一点也要考虑,那就是所有这些请求以及它们对应的响应,将会被服务器如何处理。如果我们的请求在服务器上按先来后到的顺序依次排队,那就又会导致不必要的延迟。要想实现最佳性能,就要记住以下几点:
- 使用持久连接,从 HTTP/1.0 升级到 HTTP/1.1
- 利用多个 HTTP/1.1 连接实现并行下载
- 可能的情况下利用 HTTP/1.1 管道
- 考虑升级到 HTTP/2.0 以提升性能
- 确保服务器有足够的资源并行处理请求
如果不使用持久连接,则每个 HTTP 请求都要建立一个 TCP 连接。由于 TCP 握手和慢启动,多个 TCP 会造成明显的延迟。在使用 HTTP/1.1 的情况下,最好尽可能重用已有连接。如果碰上能使用 HTTP 管道的机会,不要放过。更好的选择,则是升级到 HTTP/2.0,从而获得最佳性能。
针对 HTTP/1.1 的优化建议
针对 HTTP/1.x 的优化次序很重要:首先要配置服务器以最大限度的保证 TCP 和 TLS 的性能最优,然后在谨慎的选择和采用经典的应用最佳实践,之后在度量、迭代。
-
利用 HTTP 管道
如果你的应用可以控制客户端和服务器这两端,那么使用管道可以显著减少网络延迟。
-
采用域名分区
如果你的应用性能受限于默认的每来源 6 个连接,可以考虑将资源分散到多了来源。
-
打包资源以减少 HTTP 请求
拼接和精灵图等技巧有助于降低协议开销,又能达成类似管道的性能提升。
-
嵌入小资源
考虑直接在父文档中嵌入小资源,从而减少请求数量。
管道缺乏支持,而其他优化手段又各有各的利弊。事实上,这些优化措施如果使用不当,反倒会伤害性能。总之,要有务实的态度,通过度量来评估各种措施对性能的影响。
针对 HTTP/2.0 的优化建议
HTTP 2.0 的主要目标就是提升传输性能,实现客户端与服务器间较低的延迟和较高的吞吐量。显然,在 TCP 和 TLS 之上实现最佳性能,同时消除不必要的网络延迟,从来没有如此重要过。
接下来,或许有点意外,那就是采用移动及其他经典的最佳做法:少发数据、削减请求,根据无线网络情况调整资源供给。不管是用什么版本的协议,减少传输的数据量和消除不必要的网络延迟,对任何应用都是最有效的优化手段。
最后,杜绝和忘记域名分区、文件拼接、图片精灵等不良的习惯,这些做法在 HTTP/2.0 之上完全没有必要。事实上,继续使用这些手段反而有害,可以利用 HTTP/2.0 内置的多路分发以及服务器推送等新功能。