优化思路:
- 尽量避免发送 HTTP 请求
- 迫不得已要发送 HTTP 请求时,尽量减少请求次数
- 减少服务器的 HTTP 响应的数据大小
避免发送 HTTP 请求
这条思路不是说不发送 HTTP 请求,客户端当然要向服务器发送请求,但是呢,对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都是一样的,这个时候可以这对「请求 - 响应」缓存在本地,那么下次就可以直接读取本地的数据,不用再通过网络获取服务器的响应了,性能自然也就提升了。
因此,避免发送 HTTP 请求的方法就是通过缓存技术,在 HTTP 协议头部有不少关于缓存的字段。
缓存是如何实现的呢?
客户端会把第一次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,而响应作为 value,两者形成映射关系。这样当后续发起相同的请求时,就可以先在本地磁盘上通过 key 查到对应的 value(响应),如果命中了,就直接从本地读取该响应。显然,读取本地磁盘的速度肯定比网络请求快得多。
缓存的响应不是最新的怎么办?
服务器在发送 HTTP 响应时,会估算一个过期的时间,并把这个信息放到响应头部中,这样客户端在查看响应头部的信息时,一旦发现缓存的响应是过期的,则就会重新发送网络请求。
如果客户端从第一次请求得到的响应头部中发现该响应过期了,客户端重新发送请求,假设服务器上的资源并没有变更,那就可以不携带资源了。
具体的实现只需要客户端在重新发送请求时,在请求的 Etag
头部带上第一次请求的响应头部中的摘要,这个摘要是唯一标识响应的资源,当服务器收到请求后,会将本地资源的摘要与请求中的摘要做个比较。
- 如果不同,那么说明客户端的缓存已经没有价值,服务器在响应中带上最新的资源;
- 如果相同,说明客户端的缓存还是可以继续使用的,那么服务器仅返回不含
Body 的
304 Not Modified
响应,告诉客户端仍然有效,这样就可以减少响应资源在网络中传输的延时。
减少 HTTP 请求次数
三个方面减少 HTTP 请求次数:
- 减少重定向请求次数
- 合并请求
- 延迟发送请求
减少重定向请求次数
什么是 重定向请求?
服务器上的一个资源可能由于迁移、维护等原因从 url1
迁移至
url2
后,而客户端不知情,它还是继续请求
url1
,这时服务器返回 302
响应码和
Location
头部,告知客户端该资源已经迁移至 url2
了,于是客户端需要再发送 url2
请求以获得服务器的资源。显然,如果重定向请求越多,那么客户端就要多次发起
HTTP 请求,每一次的 HTTP 请求都得经过网络,这无疑会越降低网络性能。
此外,服务端往往不只有一台服务器,比如源服务器的上一级是代理服务器,然后代理服务器才与客户端通信,这时客户端重定向就会导致客户端与代理服务器之间需要 2 次消息传递:
如果重定向的工作交由代理服务器完成,就能减少 HTTP 请求次数了:
当代理服务器知晓了重定向规则后,可以进一步减少消息传递次数:
除了 302
重定向响应码,还有其他的响应码,参考HTTP
的重定向。
状态码 | 描述 | 意义 |
---|---|---|
301 |
Moved Permanently |
资源永久的重定向到另一个 URI 中 |
308 |
Permanent Redirect |
类似 301 的永久重定向,但是请求方法不改变 |
302 |
Found |
资源临时的重定向到另一个 URI 中 |
其中,301
和 308
响应码是告诉客户端可以将重定向响应缓存到本地磁盘,之后客户端就自动用
url2
替代 url1
访问服务器的资源。
合并请求
如果把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样,但是减少请求,也就是减少了重复发送的 HTTP 头部。
由于 HTTP/1.1 是请求响应模型,如果第一个发送的请求,未收到对应的响应,那么后续的请求就不会发送,于是为了防止单个请求的阻塞,所以一般浏览器会向同一域名的 HTTP 连接同时发起 5-6 个请求,每一个请求都是不同的 TCP 连接,那么如果合并了请求,也就会减少 TCP 连接的数量,因而省去了 TCP 握手(HTTPS 还会有 TLS 握手)和慢启动过程耗费的时间。
📢 注意:HTTP/1.1 管道模式是默认不使用的,因此讨论 HTTP/1.1 的队头阻塞问题,是不考虑管道模式的。
「具体的合并请求的几种方式」
1)通过将多个小图片合并成一个大图片来减少 HTTP 请求的次数,以减少 HTTP 请求的次数,从而减少网络的开销;
- 使用
CSS Image Sprites
技术; - 服务端使用
webpack
等打包工具将 js、css 等资源合并打包成大文件; - 将图片的二进制数据用
base64
编码后,以 URL 的形式嵌入到 HTML 文件,跟随 HTML 文件一并发送,客户端收到 HTML 后,就可以直接解码出数据,然后直接显示图片,就不用再发起图片相关的请求,这样便减少了请求的次数。
所以,合并请求的方式就是合并资源,以一个大资源的请求替换多个小资源的请求。但是这样的合并请求会带来新的问题,当大资源中的某一个小资源发生变化后,客户端必须重新下载整个完整的大资源文件,显然这带来了额外的网络消耗。
延迟发送请求
核心:按需获取。
请求网页的时候,没必要把全部资源都获取到,而是只获取当前视口页面的资源,当用户向下滑动页面的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。
减少服务器的 HTTP 响应的数据大小
对于 HTTP 的请求和响应,通常 HTTP 的响应的数据大小会比较大,也就是服务器返回的资源会比较大。此时可以考虑对响应的资源进行压缩,这样就可以减少响应的数据大小,从而提高网络传输的效率。压缩的方式一般分为 2 种:
- 无损压缩
- 有损压缩
无损压缩
无损压缩是指资源经过压缩后,信息不被破坏,还能完全恢复到压缩前的原样,适合用在文本文件、程序可执行文件、程序源代码。
1)首先,针对代码的语法规则进行压缩,因为通常代码文件都有很多换行符或者空格,这些只是为了更好的可读性,但是机器执行时并不要这些符号,把这些多余的符号给去除掉;
2)然后,进行无损压缩,需要对原始资源建立统计模型,利用这个统计模型,将常出现的数据用较短的二进制比特序列表示,将不常出现的数据用较长的二进制比特序列表示,生成二进制比特序列一般是「霍夫曼编码」算法;
gzip 就是比较常见的无损压缩。
客户端支持的压缩算法,会在 HTTP 请求中通过头部中的
Accept-Encoding
字段告诉服务器,例如:Accept-Encoding: gzip, deflate, br
;服务端收到后,会从中选择一个服务器支持的或者合适的压缩算法,然后使用此压缩算法对响应资源进行压缩,最后通过响应头部中的
Content-Encoding
字段告诉客户端该资源使用的压缩算法,比如:Content-Encoding: gzip
。
gzip 的压缩效率不如 Google 推出的 Brotil 算法,因此环境允许的情况下,优先推荐
br
。
有损压缩
与无损压缩相对的就是有损压缩,经过此方法压缩,解压的数据会与原始数据不同但是非常接近。有损压缩主要将次要的数据舍弃,牺牲一些质量来减少数据量、提高压缩比,这种方法经常用于压缩多媒体数据,比如音频、视频、图片。
可以通过 HTTP 请求头部中的 Accept
字段里的「 q
质量因子」,告诉服务器期望的资源质量,比如:Accept: audio/*; q=0.2, audio/basic
。
1)针对于图片的压缩,目前压缩比较高的是 Google 推出的WebP 格式,相同图片质量下,WebP 格式的图片大小都比 Png 格式的图片小,所以对于大量图片的网站,可以考虑使用 WebP 格式的图片,这将大幅度提升网络传输的性能。
2)针对于音视频的压缩,音视频主要是动态的,每个帧都有时序的关系,通常时间连续的帧之间的变化是很小的。
比如:一个在看书的视频,画面通常只有人物的手和书桌上的书是会有变化的,而其他地方通常都是静态的,于是只需要在一个静态的关键帧,使用增量数据来表达后续的帧,这样便减少了很多数据,提高了网络传输的性能。