网络基础20 HTTP2

网络基础之HTTP2学习笔记。

HTTP pipelining

HTTP pipelining技术指的是:把多个HTTP请求放到一个TCP连接中一一发送,在发送过程中不需要等待服务器对前一个请求的相应。但是客户端还是要按照发送请求的顺序来接受相应。

但是,服务器是要按照顺序处理请求,如果前一个请求非常耗时,那么后续请求都会受到影响,这就是所谓的线头阻塞(head-of-line blocking)。

靠大量新建连接是不能有效解决线头阻塞的问题,因为新建一个TCP连接的开销非常大。

正是由于HTTP pipelining不能彻底解决线头阻塞的问题,所以大部分桌面浏览器会默认关闭HTTP pipelining这一功能。

克服延迟的技术

在过去开发者会使用大量的技术降低延迟对用户带来的影响,其中很多技术直到现在都仍然在使用,其核心的原则就是:减少请求数(和体积),增大请求的并发数。

这些技术包括:

(1)雪碧图

(2)使用Base64格式的内联图片

(3)合并琐碎文件(将零散的JavaScript文件合并为一个大的文件,用一个请求完成下载)

(4)Domain Hash(将服务分散在尽可能多的主机上,来破解一个客户端只能和每个主机建立6-8个连接的限制)

(5)Cookie Free(将图片或其他资源分发到不同的域名,减少不必要的cookie传输)

HTTP2介绍

HTTP2就是为了解决HTTP1.1存在的各种问题,它起源于SPDY(这是一个由Google牵头开发的开源的协议)

HTTP2保留了HTTP1的URL结构,但是在实际的实现上,它只支持TLS协议,基于TLS协议进行HTTP2协商时有两种协议NPM和ALPN,二者的区别就是由谁(客户端还是服务器)来决定通信协议,ALPN让客户端发送协议优先级列表给服务器,有服务器选择合适的,NPN正好相反。

协商HTTP2协议时,会给服务器发送一个带升级(upgrade)头部的保温,如果服务器支持HTTP2就会返回101 Switching作为状态码,并且从此开始在该连接上使用HTTP2。

HTTP2的特性

(1)二进制

HTTP2是一个二进制协议,相比于基于文本的HTTP1.1,HTTP2更容易识别帧的起始和结束,此外基于二进制帧可以更便捷的从帧结构中分离出协议本身的内容。

HTTP2定义了10中不同类型的帧,最基础的两种分别对应HTTP1.1的DATAHEADERS

(2)多路复用

HTTP2连接上阐传输的每个帧都会关联到一个“流”中。流是一个独立的、双向的帧序列,数据通过流在客户端和服务端之间进行交换。

每个独立的HTTP2连接都可以包含多个并发的流,这些流中同时包含着来自两端的帧,流可以被客户端/服务端单方面的建立和使用,也可共享,或被任意一方关闭。接收方会按照收到帧的顺序来进行处理。

留的多路复用以为这在同一连接中来自各个流的数据包会被混合在一起,就像多个独立的数据车厢被拼凑到了一个一列列车上,但它们会在终点站被分开。

HTTP1.1中的keep-alive技术已经实现了在一次TCP连接中完成多个HTTP请求,但是每个请求仍然要单独发HTTP header。它与HTTP2的多路复用有何区别呢?

Keep-alive的TCP连接复用仍然是阻塞的,在同一个TCP连接上,客户端必须等到服务端响应了之后才能发起第二次请求。按顺序发送请求,按顺序接受请求,这样客户端才不会乱掉

HTTP2的TCP复用可以同时发送多个请求,不一定要按照顺序,也不用等待上一个请求的响应就可以发出下一个请求。这些请求都有唯一标识,所以不会乱掉。

HTTP/1.1不能实现多路复用是因为HTTP/1.1是基于文本分隔解析的协议, 一个HTTP/1.1的请求是通过换行符来每一条key: value的内容,这种方式存在的缺点是:

  • 以分隔符分隔消息的数据,在完成之前不能停下,所以一次只能处理一个请求或者响应
  • 解析这种数据无法预知需要多少内存,会带给服务器很大压力

而HTTP/2是基于二进制帧进行设计的,这样设计实现了一切可预知,一切可控

一帧就是一个数据单元,实现了对消息的封装,服务器解析数据帧时可以通过解析前九个字节就能知道整个帧需要多少个字节数来处理信息。

而且由于HTTP/2是分帧的,请求和相应可以交错甚至复用。

HTTP/2定义了10种不同类型的帧,来传递不同的内容。帧通过“流”来进行交换,所谓的流就是HTTP/2连接上独立的、双向的帧序列交换,在流中进行帧的双向交换,就是实现多路复用的基础:

(3)优先级和依赖性

每个流都包含一个优先级(也就是权重),它用来告诉对端哪个流更重要。当资源有限时服务器会优先处理权重较高的流。

借助PRIORITY帧,客户端会告诉服务端当前流依赖于哪个流。

优先级和依赖关系会在传输过程被动态改变,比如浏览器可以指定哪个资源有更高的优先级,或者在切换标签页的时候浏览器可以提升切换到的新页面包含的流的优先级。

(4)头压缩

HTTP是无状态的协议,所以每个请求必须携带服务器需要的所有细节,HTTP2依然如此。

这保证了的HTTP的可重复性,很多请求(例如请求页面的图片)看起来是几乎一致的,这些请求可以被压缩,同时请求中包含的cookie在很多情况下时相同的,它们也值得被压缩。

HTTP2压缩是一个很棘手的课题,即需要实现较高的压缩比,又需要放置被破解,HTTP2采用的压缩格式是HPACK,它是专门为HTTP2头部设计的压缩格式。

(5)可重置(后悔药)

HTTP1.1中,但一个HTTP信息发送后,很难去中断它(除非断开整个TPC连接),HTTP2中可以通过发送RST_STREAME帧来终止当前传输的消息并且发送一个新的消息。

(6)服务器推送

这个技术与Websocket的推送是不一样的,这个功能更准确的名称叫做“缓存推送”:但客户端请求资源时,服务器知道客户端可能也需要其他的资源,这时服务端就可以在客户端请求之前将这个资源主动推送给客户端,让客户端放进缓存中以备将来之需。

服务端推送需要客户端显式的允许服务端提供该功能,并且客户端能够通过发送一个RST_STREAME帧来主动中断推送的流。

Websocket是双向通信的协议,它也具有主动推送的功能,Websocket的主动推送和HTTP2的服务器推送有何区别呢?

HTTP2的服务器推送,推送的是静态文件(CSS/JS/HTML),而不是数据,如果需要实时的交互数据,还是需要使用Websocket的。

Websocket与HTTP2的异同:

主要区别:

  1. HTTP2推送静态文件,Websocket推送数据
  2. HTTP2使用HTTPS加密,Websocket可以使用加密协议(wss)也可以使用非加密协议(ws)
  3. HTTP2会对HTTP header进行压缩,Websocket不会

基于此,HTTP/2并不能取代Websocket技术。

(7)流量控制

每个HTTP2流都有自己的公示的流量窗口,它可以限制另一端发送数据。对于每个流来说,两端都必须告诉对方自己还有足够的空间来处理新的数据,而在该窗口被扩大前,另一端只允许被发送这么多的数据。

只有数据帧会受到流量控制。

扩展

HTTP2允许添加扩展,接收方和发送方可以再逐跳原则的基础上协商使用新的帧,但是这些帧的状态无法被改变,也不受流的控制。现在有两种广泛流行的帧可能会被纳入扩展协议中:

(1)备选服务(Alternative Service)

服务器会通过发送ALt-Svc头(或者HTTP2的ALTSVC帧)告知客户端另一个备选服务(即另外一条指向不同的服务源、主机或者端口,却能够获得同样内容的URL)

客户端收到后,应该尝试异步的去连接到该服务,如果连接成功的话,就可以使用该备选服务

(2)阻塞(Blocked)

当服务端存在需要发送的内容,但流控制却禁止发送任何数据时,那么此类型的帧将会被发送且仅发送一次。

收到这种帧,那么连接中必然有错误发生或者传输速度低于预期。

HTTP2的影响

(1)对于用户

对于用户来说,HTTP2减少了网络往返传输的数量,通过多路复用和快速丢弃无效流的办法来避免线头阻塞的困扰。

同时HTTP2支持大量并行流,所以网站的数据分发在各处也不会影响传输速度。

此外通过合理利用流的优先级,可以让客户端看可能优先收到重要的数据。

总的来说,页面载入时间和站点响应速度都会更快,简而言之,用户体验都会得到提升。

(2)对于开发者

上面提到的,有的原本广泛使用的、用来提升网络传输的速度的手段,可能会降低HTTP2的性能,例如HTTP2的多路复用倾向于使用更少的连接,所以Domain Hash可能会造成反效果,而且雪碧图和内联图片产生的优化效果也会因此降低。

但是目前HTTP2的完全部属还差的很远,所以开发者需要同时部属一套前端来支持HTTP1.1和HTTP2的访问,在两种条件下同时获得高性能,这将会是一种挑战。

HTTP2的实现

(1)浏览器

各大主流浏览器在2014年后就都路西增加了HTTP2的支持。

(2)服务器

Nginx在1.9.5版本起增加了HTTP2的支持。

NodeJS在10.0版本后正式添加了HTTP2模块,相关API可以参考文档,具体实践可以参考这篇文章《基于Node.js的HTTP/2 Server实践》

参考