Description
时值公司全面切换到HTTPS和HTTP/2,讨论HTTP/2有了更现实的意义。以前也断断续续看了些文章,做了些了解,这里算作一个学习和总结吧。
本文定位入门级别,分作两大块:
- HTTP/2是什么
- 基于HTTP/2前端可以做什么优化
本文参考了一些博文和资料,后面已列出,感谢他们的分享。
HTTP/2简介
HTTP/2 is a replacement for how HTTP is expressed “on the wire.” It is not a ground-up rewrite of the protocol; HTTP methods, status codes and semantics are the same, and it should be possible to use the same APIs as HTTP/1.x (possibly with some small additions) to represent the protocol.
HTTP/2是现行HTTP协议(HTTP/1.x)的替代,但它不是重写,HTTP方法/状态码/语义都与HTTP/1.x一样。HTTP/2基于SPDY3,专注于性能,最大的一个目标是在用户和网站间只用一个连接(connection)。
HTTP/2由两个规范(Specification)组成:
- Hypertext Transfer Protocol version 2 - RFC7540
- HPACK - Header Compression for HTTP/2 - RFC7541
为什么需要HTTP/2
我们知道,影响一个HTTP网络请求的因素主要有两个:带宽和延迟。在今天的网络情况下,带宽一般不再是瓶颈,所以我们主要讨论下延迟。延迟一般有下面几个因素:
浏览器阻塞(Head-Of-Line Blocking):浏览器会因为一些原因阻塞请求。线头阻塞(Head-Of-Line Blocking):HTTP1.x 层面上,后一个请求会被前面的请求阻塞。- DNS查询。
- 建立连接(Initial connection):HTTP基于 TCP 协议,TCP的3次握手和慢启动极大增加延迟。
说完背景,我们讨论下HTTP/1.x中到底存在哪些问题?
HTTP/1.x的缺陷
-
连接无法复用:连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对
文件类大请求大量小文件请求影响较大(没有达到最大窗口请求就被终止)。- HTTP/1.0传输数据时,每次都需要重新建立连接,增加延迟。
- 1.1虽然加入keep-alive可以复用一部分连接,但域名分片等情况下仍然需要建立多个connection,耗费资源,给服务器带来性能压力。
-
Head-Of-Line Blocking:导致带宽无法被充分利用,以及后续健康请求被阻塞。HOLB是指一系列包(package)因为第一个包被阻塞;HTTP/1.x中,由于服务器必须按接受请求的顺序发送响应的规则限制,那么假设浏览器在一个(tcp)连接上发送了两个请求,那么服务器必须等第一个请求响应完毕才能发送第二个响应——HOLB。更详细解释见下面
- 虽然现代浏览器允许每个origin建立6个connection,但大量网页动辄几十个资源,HOLB依然是主要问题。
-
协议开销大:HTTP/1.x中header内容过大(每次请求header基本不怎么变化),增加了传输的成本。
-
安全因素:HTTP/1.x中传输的内容都是明文,客户端和服务端双方无法验证身份。
HTTP/2的新特性
因为HTTP/1.x的问题,人们提出了各种解决方案。比如希望复用连接的长链接/http long-polling/websocket等等,解决HOLB的Domain Sharding(域名分片)/inline资源/css sprite
等等。
不过以上优化都绕开了协议,直到谷歌推出SPDY,才算是正式改造HTTP协议本身。降低延迟,压缩header等等,SPDY的实践证明了这些优化的效果,也最终带来HTTP/2的诞生。
1. 新的二进制格式(Binary Format)
http1.x诞生的时候是明文协议,其格式由三部分组成:start line(request line或者status line),header,body。要识别这3部分就要做协议解析,http1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑http2.0的协议解析决定采用二进制格式,实现方便且健壮。
http2的格式定义十分高效且精简。length定义了整个frame的大小,type定义frame的类型(一共10种),flags用bit位定义一些重要的参数,stream id用作流控制,payload就是request的正文。
2. Header压缩
http1.x的header由于cookie和user agent很容易膨胀,而且每次都要重复发送。
http2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。高效的压缩算法可以很大的压缩header,减少发送包的数量从而降低延迟。
3. 流(stream)和多路复用(MultiPlexing)
什么是stream?
Multiplexing of requests is achieved by having each HTTP request/response exchange associated with its own stream. Streams are largely independent of each other, so a blocked or stalled request or response does not prevent progress on other streams.
A "stream" is an independent, bidirectional sequence of frames exchanged between the client and server within an HTTP/2 connection.
A client sends an HTTP request on a new stream, using a previously unused stream identifier. A server sends an HTTP response on the same stream as the request.
Multiplexing of requests is achieved by having each HTTP request/response exchange associated with its own stream.
翻译下,stream就是在HTTP/2连接上的双向帧序列。每个http request都会新建自己的stream,response在同一个stream上返回。
多路复用(MultiPlexing),即连接共享。之所以可以复用,是因为每个stream高度独立,堵塞的stream不会影响其它stream的处理。一个连接上可以有多个stream,每个stream的frame可以随机的混杂在一起,接收方可以根据stream id将frame再归属到各自不同的request里面。
3.1 流量控制(Flow Control)
类似TCP协议通过sliding window的算法来做流量控制,http2.0使用 WINDOW_UPDATE frame 来做流量控制。每个stream都有流量控制,这保证了数据接收方可以只让自己需要的数据被传输。
3.2 流优先级(Stream Priority)
每个流可以设置优先级。优先级的目标是允许终端高速对端(当对端处理并发流时)怎么分配资源。
更重要的是,当传输能力有限时,优先级可以用来挑选哪些流(高优先级)优先传输——这是优化浏览器渲染的关键,即服务端负责设置优先级,使重要的资源优先加载,加速页面渲染。
4. Server Push
Server Push即服务端能通过push的方式将客户端需要的内容预先推送过去,也叫“cache push”。
Activity
creeperyang commentedon Dec 8, 2016
基于HTTP/2的Web优化
从HTTP/1.x到HTTP/2的通用优化规则
尽管HTTP/2相比HTTP/1.x有很大的不同,但有几条优化规则是仍然适用的:
因HTTP/2而不一样的优化规则
然后下面要介绍一些HTTP/1.x里推荐而HTTP/2禁止的优化。
Domain Sharding(域名分片):HTTP/1.x中浏览器一般每个域名最多同时使用6个连接。每个连接都会经历3次握手,会消耗服务器资源,甚至会相互堵塞。然而1.x中我们仍然使用Domain Sharding(域名分片)来突破连接数的限制(提高并行加载能力)。
但是,多少域名合适,每个连接的资源消耗,带宽竞争,DNS查询时间等等都是问题。在HTTP/2里,多路复用完美解决问题。所以请不要在HTTP/2里使用域名分片。
Concatenation(文件合并):1.x中我们经常合并文件来减少请求。但是,
所以在HTTP/2里,请避免合并文件。使用小的颗粒化的资源,优化缓存政策。
Inline resource(内联资源):内联资源也是1.x中常用的优化手段,可以减少请求。但是,内联资源无法独立缓存,也破坏了HTTP/2的多路复用和优先级策略(使用了父资源的优先级,也没法被客户端拒绝)。
HTTP/2中不要再使用内联资源,直接利用Server push:
HTTP/2需要特别考虑的优化规则
HTTP/1.1时,浏览器会通过维持一个优先级队列来给资源设置优先级,然后猜测可用TCP连接的最佳利用方式——这延迟了请求发出。
HTTP/2中,浏览器根据
type/context
来给资源设置优先级,并且在发现资源后立即发出请求。优先级以权重+依赖(weights+dependencies)的形式传达给服务端。这时候,服务端必须给重要资源设置更高优先级,更早地传给浏览器,使浏览器能获取必要资源,尽早渲染。creeperyang commentedon Dec 9, 2016
参考
截图来自 HTTP2 is here, let's optimize!
vczhan commentedon Dec 11, 2016
很有价值的文章,学习了。对1.x和2的优化规则不同的总结,帮助很大。
zhulinpinyu commentedon Dec 14, 2016
👍
w4lle commentedon Dec 14, 2016
👍
Credochen commentedon Dec 14, 2016
👍
alexclin0188 commentedon Dec 15, 2016
👍
leeweir commentedon Dec 16, 2016
不错的总结!
zhangyingwei commentedon Dec 20, 2016
很棒,学习了!
zhanglingkang commentedon Sep 25, 2017
关于HOLB的描述中:
HTTP/1.x中,由于服务器必须按接受请求的顺序发送响应的规则限制,那么假设浏览器在一个(tcp)连接上发送了两个请求,那么服务器必须等第一个请求响应完毕才能发送第二个响应。
这一段不太对吧,应该是浏览器在收到服务器的第一个响应之前不会发送第二个请求吧。 @creeperyang
creeperyang commentedon Sep 25, 2017
@zhanglingkang 可以看下文章里给的 head-of-line-blocking 链接。多个请求可以一起发送(当然有先后顺序),而服务器必须按顺序响应,所以前面的响应必然 block 后面的响应。(http1.x 的规范限制)。
h3z commentedon Feb 2, 2018
我试了一下请求200个小图片的话,http2提升非常明显。 但如果是200个大图片却比http1.1慢好多。。这个是为什么呢?☹️
是因为单个tcp连接有限制嘛?看了很多多路复用的解释,提到的都是好处,没有找到说有限制的地方。。困惑
h3z commentedon Feb 2, 2018
补充,找到一个解释:https://99designs.com/tech-blog/blog/2016/07/14/real-world-http-2-400gb-of-images-per-day/
emacsist commentedon Apr 9, 2018
很不错的总结.
zengyuangai commentedon Jan 9, 2019
慢启动则对文件类大请求影响较大 ? 慢启动影响比较大是短请求 @creeperyang
zengyuangai commentedon Jan 9, 2019
”浏览器阻塞(Head-Of-Line Blocking):浏览器会因为一些原因阻塞请求。“ 这个不对 应该http 协议规定的 不应该叫浏览器阻塞 @creeperyang
creeperyang commentedon Jan 9, 2019
@zengyuangai Head-Of-Line Blocking 的确描述的不够准确,我更新一下。
HTTP 1.0:下个请求必须在前一个请求返回后才能发出,
request-response
对按序发生。显然,如果某个请求长时间没有返回,那么接下来的请求就全部阻塞了。HTTP 1.1:尝试使用 pipeling 来解决,即浏览器可以一次性发出多个请求(同个域名,同一条 TCP 链接)。但 pipeling 要求返回是按序的,那么前一个请求如果很耗时(比如处理大图片),那么后面的请求即使服务器已经处理完,仍会等待前面的请求处理完才开始按序返回。所以,pipeling 只部分解决了 HOLB。
HTTP 2.0:通过多路复用解决了 HTTP 层面的 HOLB。
HTTP 3:通过 QUIC 解决了 tcp 层面的 HOLB。TCP 层,一个丢失的包会导致tcp暂停继续处理后面的数据包,直到这个包重传成功;QUIC 通过使用 UDP 替代 TCP,丢失的包将只影响它所在的 stream。
参考:
creeperyang commentedon Jan 9, 2019
@zengyuangai 另外慢启动影响较大的的确是小文件传输,已更正,感谢。