时值公司全面切换到HTTPS和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)组成:
我们知道,影响一个HTTP网络请求的因素主要有两个:带宽和延迟。在今天的网络情况下,带宽一般不再是瓶颈,所以我们主要讨论下延迟。延迟一般有下面几个因素:
说完背景,我们讨论下HTTP/1.x中到底存在哪些问题?
HTTP/1.x的缺陷
连接无法复用:连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。
Head-Of-Line Blocking:导致带宽无法被充分利用,以及后续健康请求被阻塞。HOLB是指一系列包(package)因为第一个包被阻塞;HTTP/1.x中,由于服务器必须按接受请求的顺序发送响应的规则限制,那么假设浏览器在一个(tcp)连接上发送了两个请求,那么服务器必须等第一个请求响应完毕才能发送第二个响应——HOLB。
协议开销大:HTTP/1.x中header内容过大(每次请求header基本不怎么变化),增加了传输的成本。
安全因素:HTTP/1.x中传输的内容都是明文,客户端和服务端双方无法验证身份。
因为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”。
尽管HTTP/2相比HTTP/1.x有很大的不同,但有几条优化规则是仍然适用的:
然后下面要介绍一些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:
With HTTP/2 the browser is relying on the server to deliver data in an optimal way --- this is critical
HTTP/1.1时,浏览器会通过维持一个优先级队列来给资源设置优先级,然后猜测可用TCP连接的最佳利用方式——这延迟了请求发出。
HTTP/2中,浏览器根据type/context
来给资源设置优先级,并且在发现资源后立即发出请求。优先级以权重+依赖(weights+dependencies)的形式传达给服务端。这时候,服务端必须给重要资源设置更高优先级,更早地传给浏览器,使浏览器能获取必要资源,尽早渲染。
原文转载至:https://github.com/creeperyang/blog/issues/23