HTTP
HTTP/0.9
HTTP/0.9 虽然简单,但是它充分验证了 Web 服务的可行性」
- 首先它只有一个命令GET。
- 它没有HEADER等描述数据的信息。因为这个时候的请求非常简单,它需要达到的目的也非常简单,没有那么多数据格式。
- 服务器发送完内容之后,就关闭TCP连接。
HTTP/1.0
随着互联网的发展,之前的HTTP/0.9已经无法满足用户需求了,浏览器希望通过HTTP来传输脚本、样式、图片、音视频等不同类型的文件,所以在1996年HTTP进行了一次版本更新:
- 增加了HEAD、POST等新方法
- 增加了响应状态码,标记可能的错误原因
- 引入了协议版本号概念
- 引入了HTTP header的概念,让HTTP处理请求和响应更加灵活
- 传输的数据不再局限于文本
HTTP/1.0最主要的缺点还是跟HTTP/0.9一样,每一个TCP连接只能发送一个HTTP请求,服务器发送完响应,就关闭连接。
为了解决这个问题,在1.0版本使用了一个非标准的Connection头部字段。当客户端再请求头部信息里面带上Connection:keep-alive的时候,服务器在发送完响应数据之后,就不会断开TCP连接了,从而达到复用同一个TCP连接的目的。但是由于不是标准字段,不同的实现可能导致表现得不一致,因此不能从根本上解决这个问题。
HTTP/1.0最核心的改变是增加了头部设定,头部内容以键值对的形式设置。请求头部通过 Accept 字段来告诉服务端可以接收的文件类型,响应头部再通过 Content-Type 字段来告诉浏览器返回文件的类型。头部字段不仅用于解决不同类型文件传输的问题,也可以实现其他很多功能如缓存、认证信息等。
HTTP/1.0 并不是一个“标准”,只是一份参考文档,不具有实际的约束力。
HTTP/1.1
为了解决 HTTP/1.0 的问题,1999 年推出的 HTTP/1.1 有以下特点:
- 长连接:引入了 TCP 连接复用,即一个 TCP 默认不关闭,可以被多个请求复用
- 并发连接:对一个域名的请求允许分配多个长连接(缓解了长连接中的「队头阻塞」问题)
- 引入管道机制(pipelining),一个 TCP 连接,可以同时发送多个请求。(响应的顺序必须和请求的顺序一致,因此不常用)
- 增加了 PUT、DELETE、OPTIONS、PATCH 等新的方法
- 新增了一些缓存的字段(If-Modified-Since, If-None-Match)
- 请求头中引入了 range 字段,支持断点续传
- 允许响应数据分块(chunked),利于传输大文件
- 强制要求 Host 头,让互联网主机托管称为可能
http/1.1的keep-alive
http 1.0中默认是关闭的,如果客户端浏览器支持Keep-Alive,那么就在HTTP请求头中添加一个字段 Connection: Keep-Alive,当服务器收到附带有Connection: Keep-Alive的请求时,它也会在响应头中添加一个同样的字段来使用Keep-Alive。
http 1.1中默认启用Keep-Alive, 默认情况下所在HTTP1.1中所有连接都被保持,除非在请求头或响应头中指明要关闭:Connection: Close ,这也就是为什么Connection: Keep-Alive字段再没有意义的原因。
HTTP管道机制(pipelining)
它指的是在一个TCP连接内,多个HTTP请求可以并行,客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务器端必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能 够区分出每次请求的响应内容。
HTTP/1.1的局限
HTTP 1.1 还是暴露出一些局限性:
- 虽然加入 keep-alive 可以复用一部分连接,但域名分片等情况下仍然需要建立多个 connection,耗费资源,给服务器带来性能压力。
- pipeling 只部分解决了队头阻塞( HOLB)。 HTTP 1.1 尝试使用 pipeling 来解决队头阻塞问题,即浏览器可以一次性发出多个请求(同个域名、同一条 TCP 链接)。 但 pipeling 要求返回是按序的,那么前一个请求如果很耗时(比如处理大图片),那么后面的请求即使服务器已经处理完,仍会等待前面的请求处理完才开始按序返回。
- 协议开销大,没有相应的压缩传输优化方案。 HTTP/1.1 在使用时,header 里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求 header 基本不怎么变化,尤其在移动端增加用户流量。
SPDY:HTTP1.X的优化(改进版HTTP/1.1)
2012年google提出了SPDY的方案,优化了HTTP1.X的请求延迟,解决了HTTP1.X的安全性,具体如下:
- 降低延迟: 针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率。
- 请求优先级(request prioritization):多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。
- header压缩:前面提到HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。
- 基于HTTPS的加密协议传输:大大提高了传输数据的可靠性。
- 服务端推送(server push):可以让服务端主动把资源文件推送给客户端。当然客户端也有权利选择是否接收。
HTTP/2.0
因为必须要保持功能上的兼容,所以 HTTP/2 把 HTTP 分解成了“语义”和“语法”两个部分,“语义”层不做改动,与 HTTP/1 完全一致(即 RFC7231)。比如请求方法、URI、状态码、头字段等概念都保留不变,这样就消除了再学习的成本,基于 HTTP 的上层应用也不需要做任何修改,可以无缝转换到 HTTP/2。
头部压缩
由于报文 Header 一般会携带“User Agent”“Cookie”“Accept”“Server”等许多固定的头字段,多达几百字节甚至上千字节,但 Body 却经常只有几十字节(比如 GET 请求、204/301/304 响应),成了不折不扣的“大头儿子”。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费,“长尾效应”导致大量带宽消耗在了这些冗余度极高的数据上。
专门的“HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率。
二进制格式
HTTP/1 里纯文本形式的报文了,它的优点是“一目了然”,用最简单的工具就可以开发调试,非常方便。
但 HTTP/2 在这方面没有“妥协”,决定改变延续了十多年的现状,不再使用肉眼可见的 ASCII 码,而是向下层的 TCP/IP 协议“靠拢”,全面采用二进制格式。
把原来的“Header+Body”的消息“打散”为数个小片的二进制“帧”(Frame),用“HEADERS”帧存放头数据、“DATA”帧存放实体数据。

虚拟的“流”
消息的“碎片”到达目的地后应该怎么组装起来呢?
HTTP/2 为此定义了一个“流”(Stream)的概念,它是二进制帧的双向传输序列,同一个消息往返的帧会分配一个唯一的流 ID。你可以想象把它成是一个虚拟的“数据流”,在里面流动的是一串有先后顺序的数据帧,这些数据帧按照次序组装起来就是 HTTP/1 里的请求报文和响应报文。
因为“流”是虚拟的,实际上并不存在,所以 HTTP/2 就可以在一个 TCP 连接上用“流”同时发送多个“碎片化”的消息,这就是常说的“多路复用”( Multiplexing)——多个往返通信都复用一个连接来处理。
在“流”的层面上看,消息是一些有序的“帧”序列,而在“连接”的层面上看,消息却是乱序收发的“帧”。多个请求 / 响应之间没有了顺序关系,不需要排队等待,也就不会再出现“队头阻塞”问题,降低了延迟,大幅度提高了连接的利用率。

为了更好地利用连接,加大吞吐量,HTTP/2 还添加了一些控制帧来管理虚拟的“流”,实现了优先级和流量控制。
server push
HTTP/2.0协议引入了Server Push机制,可以让服务端在不经过客户端请求的情况下向客户端推送资源,提高页面加载速度和性能。
具体实现步骤如下:
- 在HTTP/2.0的请求头中添加
Link字段,指定需要推送的资源。例如:
Link: </css/main.css>; rel=preload; as=style
其中,</css/main.css>表示需要推送的资源路径,rel=preload表示预加载,as=style表示资源类型为CSS。
-
服务端收到客户端请求后,解析请求头中的
Link字段,判断是否需要推送资源。 -
如果需要推送资源,则使用HTTP/2.0的推送机制,将资源推送给客户端。推送的过程类似于HTTP/2.0的请求响应过程,但是不需要等待客户端请求,可以直接推送资源。
-
客户端收到推送的资源后,可以直接使用,无需再次请求。
需要注意的是,Server Push机制并不是适用于所有场景的,需要根据具体情况进行判断和使用。同时,由于推送的资源并不是客户端主动请求的,可能会出现浪费的情况,需要权衡利弊。
http2与websocket
HTTP2虽然支持服务器推送资源到客户端,但那不是应用程序可以感知的,主要是让浏览器(用户代理)提前缓存静态资源,所以我们不能指望HTTP2可以像WebSocket建立双向实时通信。
响应无序,怎么与请求对应
HTTP/2.0引入了多路复用(Multiplexing)的机制,允许客户端同时发送多个请求,而这些请求可以在一个TCP连接上进行交错发送和接收。由于这种交错的特性,HTTP/2.0中的响应是无序的。
为了解决这个问题,HTTP/2.0引入了流(Stream)的概念。每个流代表一个独立的请求和响应。客户端和服务器可以通过流标识符(Stream Identifier)进行流的标识和区分。客户端在发送请求时分配一个唯一的流标识符,服务器在返回响应时使用相同的流标识符来标识响应属于哪个请求。
客户端可以通过流的标识符来将响应与请求对应。当客户端收到响应后,可以使用流标识符来确定响应属于哪个请求,从而完成请求和响应的对应。
xingliuhua