HTTP 2.0
HTTP/2 可以说是google早些年推出的SPDY方案的升级版,HTTP2.0 跟 SPDY 不同的地方主要是以下两点:
- HTTP2.0 支持HTTPS但也支持明文 HTTP 传输,SPDY 则强制使用 HTTPS
- HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE
关于HTTP/2的讨论仍在持续,所以不能排除会发生重大改变的可能性——《图解HTTP》。
HTTP/2 相较与HTTP/1.1 基本语义是不变的,主要的变化在于以下几点:
1、多路复用
对于HTTP/1.0,一次请求-响应 建立一个连接,每一个请求都要建立一个连接。想要并发请求的话只能通过同时建立多个连接实现,而且浏览器对于同一域名下的连接数有限制,一般是 4-8 个,超过限制的数目会被阻塞等待,这也是有些站点会有多个静态资源 CDN 域名的原因之一。
HTTP/1.1中默认采用长连接 ,通过头部Connection: keep-alive(无需显示声明,http/1.0使用长连接的话需显示声明),长连接即客户端和服务器之间的TCP连接在一段时间内保持打开状态。客户端如果长时间不发送请求的话,服务端会根据配置的超时时间来关闭连接,客户端也可以在请求的头部加上Connection:close,这样服务端在处理完响应后就会直接关闭连接,服务端也可以在响应头里设置Connection:close来告诉客户端关闭连接。虽然HTTP/1.1使用了长连接,但是客户端在发送一个请求后必须等待服务器返回该请求的响应后才能发送下一个请求。
HTTP/1.1中还提供 管道化pipeline技术,管道化指的是可以在一个连接上连续发送多个请求,服务器会按照请求的顺序依次处理请求,所以虽然请求可以一次发送多个,但是回应还是按照请求的顺序一个一个的顺序应答。一旦有请求处理的时间过长的话,后面的请求只能排队等待,这就是http/1.1中的"队头阻塞"问题,而且一些浏览器会将管道化作为一个高级配置选项,默认并不开启,所以要实现并行处理的话,要通过建立多个连接来实现。
对于HTTP/1.1来说,不管是否使用管道化技术,都会有“队头阻塞”问题:在一个连接中某个请求处理时间过长就会导致阻塞后面请求的处理。针对队头阻塞问题可以进行相应的改进措施:如浏览器会采用多TCP连接(浏览器对于同一域名下的连接数有限制,一般是 4-8 个)来分散请求;将资源分散到多个子域名以绕过浏览器对单域名的连接数限制(增加了DNS解析开销);合并 CSS/JS 文件和启用 Gzip 压缩以减少请求数量和数据量。
HTTP/2.0通过多路复用解决了队头阻塞问题:在一个连接上客户端可以发送多个请求,而服务端可以同时处理多个请求,先处理完的请求可以直接发送给客户端,如下所示。因为每个请求和响应都关联一个流ID(Stream ID),所以通过这个ID可以确保响应匹配到对应的请求。



2、二进制帧
HTTP/2流中包含了一条条帧数据(消息数据包),因为发送的请求可以并行,所以服务端收到的数据帧可能是乱序的(不同流的数据帧混杂到一起,但是每个流的数据帧是有序的),每个帧的枕头中定义了该帧属于哪个流以在接收端重新组装乱序的帧,帧头中还包含帧类型、数据长度等标识。

3、首部压缩
HTTP2.0在客户端和服务器端使用“首部表”来跟踪和存储之前发送的首部键-值对,对于每次请求-响应中相同的首部数据,不再重复发送,此时所有相同的首部都自动使用之前请求发送的首部。
如果首部发生变化了,那么只需要发送变化了数据在Headers帧里面,新增或修改的首部帧会被追加到“首部表”,如下图所示。首部表在 HTTP2.0的连接存续期内始终存在,由客户端和服务器共同渐进地更新。

4、服务端推送
http/1.1中,客户端请求html后根据解析内容再按需请求png、css、js等资源,为了减少客户端请求,可以将样式表内容写如<style>标签中、把图片改成Base64 编码的Data URL、使用preload预加载,如下所示(预加载其实只是预先下载数据供下一个网页使用,相当于提前下载,并没有减少http请求)。

http/2.0增加了服务端推送,它允许将后续需要的资源同html一起发送给客户端以减少客户端请求。浏览器如果支持http/2的话会自动接收推送的数据并缓存,在解析html后自动使用缓存的资源,如果推送的资源文件,浏览器已经有缓存的话,浏览器会优先使用本地缓存,那么推送就是浪费带宽了。
服务端开启推送的话需要进行相关配置或者配置HTTP回应的头信息Link命令,具体以及其他注意事项可以参考: 阮一峰的网络日志:HTTP/2 服务器推送。
使用http/2和websocket都可以实现服务端推送,但在浏览器中不能直接使用http/2,浏览器对于http/2中服务端推送自动处理(浏览器自动使用服务端推送过来的css、js等资源),JS中没有提供http/2相关技术,无法像fetch()或XMLHttpRequest那样发起请求后可在回调中处理服务端的回应。而JS中就提供了WebSocket对象来实现websocket通信。如果客户端要实现http/2的话,可以使用node,如下为实现单向通信(类似SSE)的代码,也可以实现像websocket那样的双向通信,http/2服务端也可以使用node来实现。其实一般我们不会直接在客户端/服务端中使用http/2来实现通信,而是使用集成了http/2的库,比如可以使用grpc来实现客户端/服务端通信。

服务端推送也可以选择SSE,它支持http/2.0、http/1.1,底层使用http(支持https),但是SSE是一种单向通信,仅支持服务端向客户端推送数据,所以适合开发订阅功能。客户端通过发起http请求并且设置头Accept:text/event-stream表明请求的不是一次性的数据包而是一个数据流,服务器响应Content-Type:text-stream,并保持连接持久化,这样就建立了SSE通话,所以SSE通信有点类似视频播放。浏览器中可以使用JS的EventSource对象来实现SSE通信。SSE默认支持断线重连,WebSocket则需要自己实现。SSE消息以文本格式传输(普通文本、JSON),websocket则支持文本和二进制(图片、视频,压缩文件等)。关于SSE的更多使用,可以参考:阮一峰的网络日志:Server-Sent Events 教程。
5、主动重置链接
HTTP消息被送出之后,我们就很难中断它了。当然,通常我们可以断开整个TCP链接(但也不总是可以这样),但这样导致的代价就是需要重新通过三次握手建立一个新的TCP连接。比如,很多app客户端都有取消图片下载的功能场景,对于http1.x来说,是通过设置tcp segment里的reset flag来通知对端关闭连接的,这种方式会直接断开连接,下次再发请求就必须重新建立连接。http2.0引入RST_STREAM类型的frame,使用它来让客户端在已有的连接中发送重置请求,这样可以在不断开连接的前提下取消某个request的stream,即中断或者放弃响应。当浏览器进行页面跳转或者用户取消下载时,它可以防止建立新连接。
6、springboot开启http/2支持

yml中配置(通用):

yml中配置(tomcat容器):


浏览器方面,新版本都支持http/2。
浙公网安备 33010602011771号