HTTP1.0
"一来一回",每个请求都是一个TCP连接(端口不一定够用),建立连接开销大,服务端没法主动推送消息。
为了解决这些问题提出了Keep-Alive机制:
客户端添加字段connection:Keep-Alive
,服务端处理完请求后不会关闭连接。但是为了防止连接数太多不释放设置了Keep-Alive timeout
字段超时取消连接。利用Content-Length:xxx
知道结束。
HTTP1.1
默认使用keep-alive,除非显式申明不用Connection:Close
(1)Chunk机制:
加上Transfer-Encoding:chunked
,告诉客户端,响应的Body是分成了一块一块的,且块与块之间有分隔符,所有块的结尾也有特殊标记。
HTTP/1.1 200 OK
...
Transfer-Encoding:chunked
25
This is the data in the first chunk
1C
sequence
0
(2)Pipeline与Head-of-line Blocking
Pipeline允许并发请求,Pipeline的Head-of-line Blocking要对请求按序处理,如果先请求的没处理则后请求的就会一直阻塞。为了避免这种问题一般默认关闭Pipeline。
HTTP1.1管道化的限制:
- 管道化要求服务端按照请求的顺序返回响应,因为请求和响应没有序号标记,无法将乱序的响应与请求关联起来。(HTTP队头阻塞)
- 客户端需要保持未收到的请求,当意外中断时重发
- 只有幂等请求才能管道化比如get和head
- HTTP2之前的服务端主动推送的解决方案:
- 客户端定期轮训
- HTTP长轮训:服务器跟客户端建立连接,但是不取消,如果服务器有新消息就立刻返回,如果没有就等一段时间再返回一个空消息之后关闭连接。
- HTTP1.1还支持下载的断点续传
- 一般浏览器对同一个域名有连接限制,最多6-8个连接。(node1.baidu.com与node2.baidu.com算两个域名)(域名分片)
HTTP2.0
HTTP2兼容HTTP1.1,位于HTTP1.1与TCP层之间,相当于一个转换层。
HTTP2的位置(1)二进制分帧
请求1分为F1,F2,F3三个帧,请求2分成F4,F5两帧,每一帧都添加了基于流的序号。每个帧在流和连接上独立传输,到达后组装成消息。允许乱序发送,再根据流标记组装。
还是无法彻底解决TCP协议自身的“队头阻塞”的问题,只是将阻塞从报文一级下沉到了帧一级。TCP队头阻塞发生在滑动窗口内,需要集齐缺失的窗口内请求,要不然不发后面的ACK。(阻塞的本质是不凑齐不提交给应用层处理)
但是二进制分帧还是降低了管道化阻塞发生的可能。因为对于返回的请求,后来的如果先分帧响应,那么先来的也无法阻塞它的响应。(响应可以认为是一次新的发送)
因为计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率。
- HTTP2可以定制流的优先级,避免阻塞高优先级的流。
HTTP/2 的数据包不是按顺序发送的,同一个连接里面连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。每个数据流都标记着一个独一无二的编号,其中规定客户端发出的数据流编号为奇数, 服务器发出的数据流编号为偶数
(2)头部压缩
在1.1图片压缩的基础上,对报文头部压缩。
HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的分。
这就是所谓的 HPACK 算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。
(3)服务器推送(鸡肋功能)
主动将客户端可能不需要的css和js在请求index.html的时候发送给对方。
- 如果不想受限于TCP的队头阻塞怎么办?
用谷歌基于UDP的QUIC协议即可。
HTTP各版本之间的区别
HTTP 1.0 | HTTP 1.1 | HTTP 2.0 |
---|---|---|
不默认http-alive | 默认支持http-alive (长连接) 支持只发送header Chunk机制 管道化(阻塞型并发) 断点续传 |
基于二进制分帧的 多路复用 (无阻塞并发) header压缩 服务器推送 定制流优先级 |
网友评论