微软学习打车软件也开始抢占出租车,拟将后座上登陆Office(出租车司机给微软上课)
731
2022-05-28
问你一句:「你知道 HTTP/1.1 该如何优化吗?」
我想你第一时间想到的是,使用 KeepAlive 将 HTTP/1.1 从短连接改成长链接。
这个确实是一个优化的手段,它是从底层的传输层这一方向入手的,通过减少 TCP 连接建立和断开的次数,来减少了网络传输的延迟,从而提高 HTTP/1.1 协议的传输效率。
但其实还可以从其他方向来优化 HTTP/1.1 协议,比如有如下 3 种优化思路:
尽量避免发送 HTTP 请求;
在需要发送 HTTP 请求时,考虑如何减少请求次数;
减少服务器的 HTTP 响应的数据大小;
下面,就针对这三种思路具体看看有哪些优化方法。
1 如何避免发送 HTTP 请求?
这个思路你看到是不是觉得很奇怪,不发送 HTTP 请求,那还客户端还怎么和服务器交互数据?小林你这不是耍流氓嘛?
冷静冷静,你说的没错,客户端当然要向服务器发送请求的。
但是,对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。
所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。
那缓存是如何做到的呢?
客户端会把第一次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,而响应作为 value,两者形成映射关系。
这样当后续发起相同的请求时,就可以先在本地磁盘上通过 key 查到对应的 value,也就是响应,如果找到了,就直接从本地读取该响应。毋庸置疑,读取本次磁盘的速度肯定比网络请求快得多,如下图:
聪明的你可能想到了,万一缓存的响应不是最新的,而客户端并不知情,那么该怎么办呢?
放心,这个问题 HTTP 设计者早已考虑到。
所以,服务器在发送 HTTP 响应时,会估算一个过期的时间,并把这个信息放到响应头部中,这样客户端在查看响应头部的信息时,一旦发现缓存的响应是过期的,则就会重新发送网络请求。HTTP 关于缓说明会的头部字段很多,这部分内容留在下次文章,这次暂时不具体说明。
如果客户端从第一次请求得到的响应头部中发现该响应过期了,客户端重新发送请求,假设服务器上的资源并没有变更,还是老样子,那么你觉得还要在服务器的响应带上这个资源吗?
很显然不带的话,可以提高 HTTP 协议的性能,那具体如何做到呢?
只需要客户端在重新发送请求时,在请求的 Etag 头部带上第一次请求的响应头部中的摘要,这个摘要是唯一标识响应的资源,当服务器收到请求后,会将本地资源的摘要与请求中的摘要做个比较。
如果不同,那么说明客户端的缓存已经没有价值,服务器在响应中带上最新的资源。
如果相同,说明客户端的缓存还是可以继续使用的,那么服务器仅返回不含有包体的 304 Not Modified 响应,告诉客户端仍然有效,这样就可以减少响应资源在网络中传输的延时,如下图:
缓存真的是性能优化的一把-,小到 CPU Cache、Page Cache、Redis Cache,大到 HTTP 协议的缓存。
2 如何减少 HTTP 请求次数?
减少 HTTP 请求次数自然也就提升了 HTTP 性能,可以从这 3 个方面入手:
减少重定向请求次数;
合并请求;
延迟发送请求;
我们先来看看什么是重定向请求?
服务器上的一个资源可能由于迁移、维护等原因从 url1 移至 url2 后,而客户端不知情,它还是继续请求 url1,这时服务器不能粗暴地返回错误,而是通过 302 响应码和 Location 头部,告诉客户端该资源已经迁移至 url2 了,于是客户端需要再发送 url2 请求以获得服务器的资源。
那么,如果重定向请求越多,那么客户端就要多次发起 HTTP 请求,每一次的 HTTP 请求都得经过网络,这无疑会越降低网络性能。
另外,服务端这一方往往不只有一台服务器,比如源服务器上一级是代理服务器,然后代理服务器才与客户端通信,这时客户端重定向就会导致客户端与代理服务器之间需要 2 次消息传递,如下图:
如果重定向的工作交由代理服务器完成,就能减少 HTTP 请求次数了,如下图:
而且当代理服务器知晓了重定向规则后,可以进一步减少消息传递次数,如下图:
除了 302 重定向响应码,还有其他一些重定向的响应码,你可以从下图看到:
其中,301 和 308 响应码是告诉客户端可以将重定向响应缓存到本地磁盘,之后客户端就自动用 url2 替代 url1 访问服务器的资源。
如果把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样,但是减少请求,也就意味着减少了重复发送的 HTTP 头部。
另外由于 HTTP/1.1 是请求响应模型,如果第一个发送的请求,未收到对应的响应,那么后续的请求就不会发送,于是为了防止单个请求的阻塞,所以一般浏览器会同时发起 5-6 个请求,每一个请求都是不同的 TCP 连接,那么如果合并了请求,也就会减少 TCP 连接的数量,因而省去了 TCP 握手和慢启动过程耗费的时间。
接下来,具体看看合并请求的几种方式。
有的网页会含有很多小图片、小图标,有多少个小图片,客户端就要发起多少次请求。那么对于这些小图片,我们可以考虑使用 CSS Image Sprites 技术把它们合成一个大图片,这样浏览器就可以用一次请求获得一个大图片,然后再根据 CSS 数据把大图片切割成多张小图片。
这种方式就是通过将多个小图片合并成一个大图片来减少 HTTP 请求的次数,以减少 HTTP 请求的次数,从而减少网络的开销。
除了将小图片合并成大图片的方式,还有服务端使用 webpack 等打包工具将 js、css 等资源合并打包成大文件,也是能达到类似的效果。
另外,还可以将图片的二进制数据用 base64 编码后,以 URL 的形式潜入到 HTML 文件,跟随 HTML 文件一并发送.