3

最近读完《HTTP2 基础教程》,相当于是对 HTTP2 有了个大致的了解,本文已加入我的博客,感谢大家支持!

HTTP 发展史

互联网刚开始的时候,人们浏览网页只是为了阅读文字,随着时代的不断发展,人们对于网站的需求越来越高,一个网站不仅仅要展示文字,还要展示图片,视频,3D 特效等。

1、Web 页面引用的内容每年都在增长,图片,JS,CSS 越来越大,也越来越多。

2、Web 所依赖的资源也变得越来越复杂。

3、大多数 Web 页面会关联数十个域名的资源,每一个资源都需要经历一次 DNS,TCP,TLS 等。

HTTP1.1 阶段

人们对网站的需求越来越多,但是 HTTP 协议却发展很慢,HTTP1.1 已经存在 20 年了,却还是当前社会中使用最广泛的协议。以下几个缺点也越来越被大家所关注:

1、队头阻塞,在请求响应中如果出现任何问题,剩下的工作都会被阻塞在那次请求应答之后。

2、低效的 TCP 利用

拥塞窗口:在接收方确认数据包之前,发送方可以发出的 TCP 包的数量。(例如:如果拥塞窗口为 1,那么发送方发出 1 个数据包之后,只有接收方确认了那个包,才能发送下一个)。

TCP 中有个概念叫慢启动,目的是为了让新连接搞清楚当前的网络状况,避免给已经拥堵的网络继续添乱。(它允许客户端在收到每个确认回复后额外发送 1 个未确认包,这意味着新连接在收到 1 个确认回复后,可以发送 2 个数据包,在收到 2 个确认回复后,可以发 4 个,以此类推,直到达到上限值)

3、臃肿的消息首部

虽然 HTTP1 提供了压缩请求内容的机制,但是消息首部却无法压缩,如果算上 cookie,可能每次都会多发送几千个字节。

4、受限制的优先级设置

HTTP1 没有优先级一说,要么发起请求,要么不发起。

Web 性能调优

为了解决 HTTP1 的各种问题,大家也总结一下优化方案,我们来依次介绍一下吧。

网站运行流程(简化版)

  1. 输入 URL 并敲回车。
  2. 根据域名解析 IP 地址。
  3. 建立 TCP 连接管道。
  4. 如果是 HTTPS,进行 TLS 握手。
  5. 服务器端收到请求。
  6. 输出主体 HTML 。
  7. 客户端根据 HTML 内的其他资源进行请求。

如果想在 HTTP 协议层面做优化,可以考虑下几点:

  • DNS 查询时间
  • TCP 三次握手时间
  • TLS 安全协议时间(秘钥协商,对称加密,非对称加密)

DNS 查询优化

  • 限制不同域名数量
  • 使用 dns-prefetch,在解析主体 HTML 的同时,就会解析制定域名。
<link rel="dns-prefetch" href="//www.xxx.com" />
  • 找一家好的外部供应商

优化 TCP 连接

  • 使用 preconnect 指令,让连接在使用之前就已经建立好。
<link rel="preconnect" href="//www.xxx.com" />
  • 尽早终止响应(借助 CDN,让传输更近)。

避免重定向

  • 利用 CDN 云端重定向。
  • 统一域名使用 Web 服务器上的 rewrite 规则,避免重定向。

缓存策略

我们常常使用缓存来避免不必要的请求,但要使用缓存必须遵守着两个概念:多用户之间可共享能够接受一定程度的旧数据

  • 纯静态不变的内容,可以永久缓存。
  • CSS/JS 等个性化资源,缓存时间是会话(交付)平均时间的两倍。
强缓存

使用 Expires 首部,将资源失效的日期告诉客户端,在失效如期之前,客户端都会直接使用缓存中的资源而不会发起请求

使用 Cache-Control 首部,进行更多的定制化缓存:

  • max-age 表示资源会缓存的具体时间
  • no-cache 不使用本地缓存。需要使用缓存协商。
  • no-store 直接禁止游览器缓存数据,每次用户请求该资源,都会向服务器发送一个请求,每次都会下载完整的资源。
  • public 可以被所有的用户缓存,包括终端用户和 CDN 等中间代理服务器。
  • private 只能被终端用户的浏览器缓存,不允许 CDN 等中继缓存服务器对其缓存。
协商缓存

在请求中包含 HTTP 首部 Last Modified,仅当最新内容在首部中制定的日志之后被更新过,服务器才返回完整内容,否则返回 304.

在请求实体中包含实体校验码 ETag,它唯一标识所请求的资源,服务器会比较当前 ETag 与请求首部中收到 ETag,如果一致,就返回 304.

压缩和代码简化

删除 html 文档中的注释,换行,空格等,减少资源大小。

避免阻塞 CSS/JS

由于 JS 在处理时,会阻止其他任何资源的下载和渲染,可能会带来不必要的延迟。

  • CSS 影响页面可视化效果,建议先请求。
  • JS 则需要正确用好 defer, async。

图片优化

一张图片中往往并不是只有图片内容,往往还包含有一些图片元信息:地理位置信息,时间戳,尺寸,像素等。而这些信息对于页面并没有声明用,还会增加图片的大小,建议删除掉图片元信息,只保留真正有用的部分。

另外,页面中的图片,尽量不要使用 CSS 去拉伸或者缩放,需要多大的图片,就返回多大的图,避免资源浪费。

回归正题 HTTP2 核心概念

上面介绍了 HTTP 的历史以及现阶段使用最广泛的 HTTP1 ,接下来我们来介绍一下 HTTP2 ,看一看 HTTP2 是怎么解决 HTTP1 所遗留的问题?

首先,要搭建 HTTP2 服务,必须配套使用 HTTPS 安全策略,才能得到浏览器的支持。HTTP2 其实并没有要求必须使用 HTTPS,只是人们正好认识到 HTTPS 的重要性,而 HTTP2 正好出来,就不谋而合的结合在一起了

HTTP2 分层

  • 分帧层,即 HTTP2 多路复用能力的核心部分。
  • 数据或 HTTP 层,其中包含传统上被认为是 HTTP 及其相关数据的部分。

分帧会来来如下好处:

1、二进制协议:HTTP2 的分帧层是基于帧的二进制协议,方便机器解析。

2、首部压缩:仅仅使用二进制协议还不够,HTTP2 的首部还会被深度压缩。

3、多路复用:当你在使用 HTTP2 传输链接的时候,不必等待上一个请求结束后才进行下一个请求,请求和响应可以交织在一起。

HTTP2 是基于帧的协议,为了将重要的信息都封装起来,让协议的解析方可以轻松的阅读、解析并还原信息。相比之下,HTTP1 并不是基于帧的,而是以文本来分隔,服务器只能根据文本换行符来拆分请求数据。

使用 HTTP1 可能会产生以下问题:

  • 换行符对于每个平台可能有兼容性问题,例如有些平台采用<crlf>,有些平台采用<lf>
  • 一次只能处理一个请求或响应,完成之前不能停止解析。
  • 无法预判解析需要多少内存,如果一行的信息量太大,超出了内存,会返回 400 错误

帧有着严格的结构格式,有了帧,处理协议的程序就能预先知道会受到什么,从而采取对应的解析方法。可以把帧理解为一个对象:

var frame = {
    length:'帧负载的长度',
    Type:'类型',
    Flags:'帧的标识',
    R:'保留位',
    Stream Identifer :'每个流唯一ID',
    Frame Payload:'真实的帧内容'
}

这样以来,实现和维护都会简单很多,不用等到一个请求完成以后才进行下一次请求,请求和响应可以交错甚至可多路复用。

流是 HTTP2 链接上独立的,双向的帧序列交换。可以将流看作在连接上的一些列帧,他们构成了单独的 HTTP 请求和响应。

1、消息:泛指 HTTP 中一个请求或一个响应。

2、流量控制:当一段接收并消费被发送的数据时,它将发出一个 WINDOW_UPDATE 帧,用来表示其更新后的处理字节的能力。

确保一个流不会影响到其他的流。

3、优先级

首先请求网页上最重要的元素,以最优的顺序获取资源,由此来优化页面性能。

  • 通过 HEADERS 帧可以指明某些对象和其他对象的依赖关系。
  • 通过 PRIORITY 帧,可以告诉服务器如何确定具有共同依赖关系的对象的优先级。

服务器推送

提升单个资源性能的最佳方式,就是在它被用到之前就放到了浏览器的缓存里,服务器端可以主动将资源发给客户端,这可能是因为它知道客户端不久后将会用到该资源。

如果服务器决定推送一个对象,会通过 PUSH_PROMISE 帧去传递将会被推送的资源。

首部压缩

之前说到,HTTP1 中没有对首部进行压缩,这会在每次请求中发送大量的冗余首部,而 HTTP2 则解决了这个问题。HTTP2 没有使用 gzip 压缩,而是使用 HPACK,因为 GZIP 压缩有泄漏加密信息的风险,简称 CRIME。

CRIME 原理:攻击者在请求中添加数据,观察压缩加密后的数据量,如果变小了,就证明注入的数据和请求中的其他内容有重复,进而搞清楚所有的加密数据内容。

HTTP2 的 HPACK 原理:

当客户端请求时,会根据发送的首部信息,建立一张表:

索引 首部名称
62 header1 foo
63 header2 bar
64 header3 baz

服务器端再接收到数据的时候也会创建一张与之对应的表。客户端在发送下一个请求的时候,如果首部是相同的,就不用发送 Header 头,只用发送索引号就行了,服务器端会索引去还原对应的首部信息。

并非一定能从 HTTP2 中获益

之前说了这么多 HTTP2 的好处,你是否已经觉得升级 HTTP2 已经迫不及待了呢?但是 HTTP2 页不是完美的,这一小节我们来介绍一下 HTTP2 的一些坑。

1、关于丢包。之前说到,HTTP2 采用多路复用,可以让多个请求在同一个 TCP 连接中进行传输,但是由于 HTTP2 是单链接架构,如果唯一的连接发生了丢包,所有的工作都会受到影响,这其实是 HTTP2 中比较大的坑。

HTTP1 在请求时建立了多个连接(和浏览器相关,一般为 6 个),相对于 TCP 的初始拥塞窗口更大。当有一个连接发生丢包时,不会影响到其他请求。对比之下: HTTP2 比 HTTP1 更容易丢包。

2、关于服务器端推送。之前说到,服务器端推送可以主动给客户端推送资源,用来减少客户端发起请求数量。可是这也可能带来一个问题:如果推送的资源在客户端已经缓存过,那就是多此一举了,所以在做服务器端渲染时,一定要和客户端的缓存策略结合起来。

HTTP2 反模式

HTTP2 部署之后,之前针对于 HTTP1 的优化方案有可能就不适用了,我们来看一下都有哪些。

  • 域名拆分

在 HTTP1 中,会将一些静态资源存在多个 cdn 服务器(因为浏览器对同一个域名下的资源请求并发数有限制,一般是 6 个)。但是使用 HTTP2 之后就不必要了,因为 HTTP2 采用多路复用,再多的资源也都能够进行并发请求。

  • 资源内联

在 HTTP1 中,我们可会对一些小图片直接打包成 base64 格式,用来减少请求。但内联无法利用缓存优势,应具体情况具体分析。

  • 资源合并(雪碧图等)

由于多路复用的原因,多个小图片可以并行请求,雪碧图也不是很有必要了。

  • 禁用带 cookie 的域名

在 HTTP1 中,由于无法压缩首部,会启动一个无 cookie 的服务器专门用来存放某些静态资源,用来减少不必要的 cookie 传输。在 HTTP2 中,首部信息会被 HPACK 算法优化,大大减少了首部字节,而且不用心增一个无 cookie 的服务器,所以建议取消掉禁用 cookie 域名的方式。

  • 资源预取

高数浏览器,之哟啊有可能就继续下载可缓存资源,并把这些资源缓存起来,这个还是有必要。

<link rel="prefetch" href="xxx.css">

展望未来

1、HTTP 是使用 TCP 作为传输层的协议,由于 TCP 是可靠的,拥塞控制的协议,在进行一次连接时,会发生三次握手,断开连接时会有 4 次挥手,所以有人提出可以用 UDP 这种简单,快速的协议去代替。

  • Google 开发的 QUIC 是极快的 UDP 网络连接,提供了 HTTP2 等效的,多路复用,流量控制,TSL 等效的安全机制,以及 TCP 等效的连接语义、可靠性、拥塞机制。

2、TLS(传输层安全协议,用来实现 HTTPS)也在不断地进行改进。在 TLS1.3 版本中 新连接只需要一次往返(目前最少是三次),如果是恢复连接,则不需要往返延时。


孟思行
2.1k 声望2.4k 粉丝