web
性能权威指南的读书笔记,加深一些理解和记忆。
# http2
的目的
支持请求和响应的
多路复用
来减少网络延迟。通过压缩
http
首部字段将协议开销降至最低。支持请求
优先级
。服务端推送
能力。
# 增强性能的原因
# 二进制分帧层
协议定义了如何封装 http
的消息在客户端和服务端之间传输。http1.x
是以换行符作为纯为本的分隔符,而 http2.0
是将传输信息分割为更小的消息和帧,并且对它们进行二进制编码。所以只有客户端和服务端同时理解 http2.0
的两者才能进行通信。
说一下 流、消息、帧之间的关系。
流
:在已经建立的连接上的双向字节流。消息
:完整的一系列的数据帧。帧
:http2.0
中通信的最小单位,每个帧包含首部,至少会标识出当前的帧是属于哪个流。
串起来的一个流程大概就是: http2.0
协议连接之后,开始通信,其根本是在一个 tcp
的连接上完成的数据传输,在这个连接上,承载着任意数量的双向数据流
。其中每一个数据流都是以消息【消息指代 http
的一些动作,请求、响应等】的方式发送的,而消息却是由一个或多个帧构成,其中这些帧都是以乱序发送
的,在接受完数据之后,会根据帧的首部标识符来重新组装
。
分析一下二进制分帧中最常见的两种工作流:
# 发起新流
建立完
http2.0
的连接之后,在发起数据之前,必须要创建一个新的流来承载消息,流中需要包含资源的优先级、http
首部等信息。http2.0
的规定,客户端和服务端都可以发起新流,只是两者的方式不同。- 客户端发送
HEADERS
帧来发起新流,其发起的流是具有奇数
的ID
。 - 服务端发送
PUSH_PROMISE
帧来发起推流,其发起的流是具有偶数
的ID
。
这样客户端和服务端每次发送流时候都会递增
ID
,两端的流并不会冲突。- 客户端发送
# 交换应用数据
创建完新流并且发送完
http
首部之后,接下来就是发送应用数据,数据不会被另行编码或者压缩,编码方式取决于应用或者服务器,在发送数据的时候,每帧的大小最多可以达到2^16 - 1
字节,但是为了减少队首阻塞,http2.0
要求数据不能超过2^14 - 1
,如果数据超出,就需要分为多帧传输。# 多向请求和响应
在 http1.x
中,如果客户端想发起多个并行的请求,那必须打开多个 tcp
的连接,但是其中多个响应的导致的最直接的问题就是排队,并且会导致队首阻塞
,结果就是导致 tcp
的连接效率底下。
http2.0
中二进制分帧突破了这一些限制,其实现了多向请求和响应,客户端和服务端都可以将 http
消息分解为互不依赖的帧,然后乱序在连接上发送,最后重新组合。这一增强带来的是巨大的性能提升,因为多个请求并行且互不影响、互不干扰、只需要一个连接就可以并行发送多个请求和响应、消除了 http1.x
中不必要的延迟、队首阻塞以及多个 tcp
连接抵效的问题。在同等的情况下,http2.0
只需要一个 tcp
连接就可以实现并行等,看似减少了 tcp
的连接数,其实最直接的是减少了服务端的 cpu
和内存的消耗。
# 请求优先级
每一个在连接上传输的流都带有一个 31
比特的优先值,其中 0
最大,2^31 - 1
最小,服务端可以根据客户端资源请求的优先级来控制资源的分配
(cpu
、内存、带宽),在数据准备好之后,将优先级最高的数据先发送给客户端。在客户端明确设置好各种资源的优先级之后,服务端应该按照优先级要求将资源依次返回,这样排队的问题基本上就会解决掉。
# 一个连接
http2.0
的所有传输都是在一个 tcp
的连接上完成,大多数的 http
连接都是突发并且短暂,但是因为 tcp
的慢启动
以及流量拥塞窗口
导致在经过一段时间之后才会很好的利用带宽,效率变高。而 http2.0
从头到尾的一个连接可以最大化和更有效的使用 tcp
的连接、减少网络延迟、提高吞吐量等。
# 流量控制
在 http2.0
连接建立之后,客户端和服务端会交换帧
,其目的是设置双向的流量控制窗口的大小
,从而控制流占用的资源,接收方针对特定的流动态调整窗口大小来限制或者提高传输速度。
# 服务端推送
顾名思义,服务端可以直接向客户端发送响应,在上边的流量控制中,交换帧之后,客户端会收到 PUSH_PROMISE
的帧,客户端可以视自己需求来处理,限定推送流的数量或者设置 0
禁止服务端推送。如果服务端已经预先知道了客户端即将需求请求什么资源,那么就可以直接推送给客户端资源提前加载,减少在需求的时候请求带来的延迟。并且服务端推送过来的数据是可以直接进行缓存
的。但是服务端推送的缺点,其要遵守 请求-响应
的顺序,不能随意的发起推送,并且 PUSH_PROMISE
的帧必须在返回响应之前发送,避免出现竞态条件出现浪费资源的请求,也就是说要确保客户端的请求不正好是服务端推送的就行。
# 首部压缩
http1.x
每次请求都需要携带头部的元数据,再加上一个 cookie
信息,仅一次请求不携带别的数据,固定荷载就可能会接近上千字节,为了减少这个开销,http2.0
进行了首部元数据的压缩。具体的就是,每次的发送,对于相同的数据不再发送,http2.0
的首部在一个连接内持续存在,并且信息是由双端共同更新。如果有新的首部键值对,要么在尾部追加,要么就是替换之前的。所以下一次的请求发起之前,如果发现首部没变化,就不会再发送首部,这会极大减少请求携带数据的开销。举例: http1.x
的轮训,每次请求固定的头部开销可能回到 800k
,而 http2.0
之后,可能第一次发起请求的时候会发送头部数据,之后的所有请求都不会再发送,因为后边的首部都是自动使用之前请求发送的首部。