跳到主要内容

版本

2023年12月23日
柏拉文
越努力,越幸运

一、HTTP 0.9


1991HTTP 0.9版,只有一个GET,而且只支持纯文本内容,早已过时就不讲了

二、HTTP 1.0


2.1 特点

  • 任意数据类型都可以发送

  • GETPOSTHEAD三种方法

  • 无法复用TCP连接(长连接)

  • 有丰富的请求响应头信息。以header中的Last-Modified/If-Modified-SinceExpires作为缓存标识

三、HTTP 1.1


3.1 特点

优点:

  • 持久连接persistent connection),即TCP连接默认不关闭,可以被多个请求复用,不用声明Connection: keep-alive。长连接的连接时长可以通过请求头中的 keep-alive 来设置

  • 管道机制pipelining),即在同一个TCP连接里,客户端可以同时发送多个 请求,进一步改进了HTTP协议的效率。

  • 缓存机制 HTTP 1.1 中新增加了 E-tagIf-Unmodified-Since, If-Match, If-None-Match 等缓存控制标头来控制缓存失效。

  • 断电续传 支持断点续传,通过使用请求头中的 Range 来实现。

  • 虚拟网络 使用了虚拟网络,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。

  • 新增方法 PUTPATCHOPTIONSDELETE

缺点:

  • 在传输数据过程中,所有内容都是明文,客户端和服务器端都无法验证对方的身份,无法保证数据的安全性。

  • HTTP/1.1 版本默认允许复用TCP连接,但是在同一个TCP连接里,所有数据通信是按次序进行的,服务器通常在处理完一个回应后,才会继续去处理下一个,这样子就会造成队头阻塞。同一个 TCP 管道中同一时刻只能处理一个 HTTP 请求,也就是说如果当前请求没有处理完,其它的请求都处于阻塞状态,另外浏览器对于同一域名下的并发请求数量都有限制,比如 Chrome 中只允许 6 个请求并发(这个数量不允许用户配置),也就是说请求数量超过 6 个时,多出来的请求只能排队、等待发送。

  • **Http/1.x**版本支持 Keep-alive,用此方案来弥补创建多次连接产生的延迟,但是同样会给服务器带来压力,并且的话,对于单文件被不断请求的服务,Keep-alive会极大影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。

3.2 长连接

HTTP keep-alive也称为 HTTP 长连接, 它通过重用一个 TCP 连接发送/接收多个 HTTP请求,来减少创建/关闭多个 TCP 连接的开销。我们知道 HTTP协议采用请求-应答模式,当使用普通模式,即KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成 之后立即断开连接(HTTP协议为无连接的协议);当使用 Keep-Alive模式(又称持久连接、连接重用)时,**Keep-Alive功能使客户端到服 务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive**功能避免了建立或者重新建立连接。

Preview

如图:短连接极大的降低了传输效率

什么是keep-alive

**keep-alive**是客户端和服务端的一个约定,如果开启 keep-alive,则服务端在返回 **Response**后不关闭 TCP 连接;同样的,在接收完响应报文后,客户端也不关闭连接,发送下一个 HTTP 请求时会重用该连接。

为什么要使用keep-alive

**keep-alive**技术的创建目的,能在多次HTTP之前重用同一个TCP连接,从而减少创建/关闭多个 TCP 连接的开销(包括响应时间、CPU 资源、减少拥堵等)

如可开启keep-alive

  • 在 **HTTP/1.0**协议中,默认是关闭的,需要在http头加入Connection: Keep-Alive,才能启用Keep-Alive

    Connection: keep-alive
  • **Http 1.1**中默认启用Keep-Alive,如果加入Connection: close ,才关闭

    Connection: close

长连接优点

  • 减少 CPU 及内存的使用,因为不需要经常建立和关闭连接
  • 支持管道化的请求及响应模式
  • 减少网络堵塞,因为减少了 TCP 请求
  • 减少了后续请求的响应时间,因为不需要等待建立 TCP、握手、挥手、关闭 TCP 的过程
  • 发生错误时,也可在不关闭连接的情况下进行错误提示

长连接缺点

一个长连接建立后,如果一直保持连接,对服务器来说是多么的浪费资源呀,而且长连接时间的长短,直接影响到服务器的并发数还有就是可能造成队头堵塞(下面有讲),造成信息延迟

如何避免长连接资源浪费?

  • 客户端请求头声明:Connection: close,本次通信后就关闭连接

  • 服务端配置:如 Nginx,设置**keep-alive_timeout设置长连接超时时间,keep-alive_requests**设置长连接请求次数上限

  • 系统内核参数设置:

    • net.ipv4.tcp_keepalive_time = 60,连接闲置 60 秒后,服务端尝试向客户端发送侦测包,判断 TCP 连接状态,如果没有收到 ack 反馈就在
    • net.ipv4.tcp_keepalive_intvl = 10,就在 10 秒后再次尝试发送侦测包,直到收到 ack 反馈,一共会
    • net.ipv4.tcp_keepalive_probes = 5,一共会尝试 5 次,要是都没有收到就关闭这个 TCP 连接了

3.3 管道化

管道化/管线化:

**http1.1**在使用长连接的情况下,建立一个连接通道后,连接上消息的传递类似于

请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3

管线化连接的消息就变成了类似这样

请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

管线化是在同一个 TCP 连接里发一个请求后不必等其回来就可以继续发请求出去,这可以减少整体的响应时间,但是服务器还是会按照请求的顺序响应请求,所以如果有许多请求,而前面的请求响应很慢,就产生一个著名的问题队头堵塞

管线化的特点:

  • 管线化机制通过持久连接完成,在 http1.1 版本才支持
  • 只有 GET 请求和 HEAD 请求才可以进行管线化,而 POST 有所限制
  • 初次创建连接时不应启动管线化机制,因为服务器不一定支持 http1.1 版本的协议
  • 管线化不会影响响应到来的顺序,如上面的例子所示,响应返回的顺序就是请求的顺序
  • 要求客户端和服务端都支持管线化,但并不要求服务端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
  • 由于上面提到的服务端问题,开户管线化很可能并不会带来大幅度的性能提升,而且很多服务端和代理程序对管线化的支持并不好,因为浏览器(Chrome/Firefox)默认并未开启管线化支持

3.4 对头阻塞

对头阻塞

http1.0 协议采用的是请求-应答模式,报文必须是一发一收,就形成了一个先进先出的串行队列,没有轻重缓急的优先级,只有入队的先后顺序,排在最前面的请求最先处理,就导致如果队首的请求耗时过长,后面的请求就只能处于阻塞状态,这就是著名的队头阻塞问题

如何解决 HTTP 的队头阻塞问题?

  • 并发连接

    因为一个域名允许分配多个长连接,就相当于增加了任务队列,不至于一个队列里的任务阻塞了其他全部任务。以前在RFC2616中规定过客户端最多只能并发2个连接,但是现实是很多浏览器不按套路出牌,就是遵守这个标准T_T,所以在RFC7230把这个规定取消掉了,现在的浏览器标准中一个域名并发连接可以有6~8个,记住是6~8个,不是6个(Chrome6个/Firefox8个)
  • 域名分片

    一个域名最多可以并发6~8个,那咱就多来几个域名
    比如a.baidu.com,b.baidu.com,c.baidu.com,多准备几个二级域名,当我们访问baidu.com时,可以让不同的资源从不同的二域名中获取,而它们都指向同一台服务器,这样能够并发更多的长连接了

四、HTTP 版本-SPDY


SPDY(读作“SPeeDY”)是Google开发的基于TCP的会话层协议

主要通过帧、多路复用、请求优先级、HTTP报头压缩、服务器推送以最小化网络延迟,提升网络速度,优化用户的网络使用体验

原理是在SSL层上增加一个SPDY会话层,以在一个TCP连接中实现并发流。通常的HTTP GET和POST格式仍然是一样的,然而SPDY为编码和传输数据设计了一个新的帧格式。因为流是双向的,所以可以在客户端和服务端启动

虽然诞生后很快被所有主流浏览器所采用,并且服务器和代理也提供了支持,但是SPDY核心人员后来都参加到HTTP 2.0开发中去了,自HTTP2.0开发完成就不再支持SPDY协议了,并在Chrome 51中删掉了SPDY的支持

五、HTTP 版本-2.0


5.1 特点

优点:

  • 头部压缩: HTTP 1.1版本会出现 User-AgentCookieAcceptServerRange 等字段可能会占用几百甚至几千字节,而 Body 却经常只有几十字节,所以导致头部偏重。HTTP 2.0 使用 HPACK 算法进行压缩。

  • 多路复用: 复用TCP连接, 将数据分为多个二进制帧,多个请求和响应的数据帧在同一个 TCP 通道进行传输, 且不用按顺序一一对应,这样子解决了队头阻塞的问题。而与此同时,在 HTTP2 协议下,浏览器不再有同域名的并发请求数量限制,因此请求排队问题也得到了解决。

  • 服务器推送: 允许服务器未经请求,主动向客户端发送资源,即服务器推送。即服务端推送能力。可以让某些资源能够提前到达浏览器,比如对于一个 html 的请求,通过 HTTP 2 我们可以同时将相应的 jscss 资源推送到浏览器,省去了后续请求的开销。

  • 二进制分帧: 这是一次彻底的二进制协议,头信息和数据体都是二进制,并且统称为"帧":头信息帧和数据帧。使用新的二进制协议,不再是纯文本,避免文本歧义,缩小了请求体积

  • 请求优先级: 可以设置数据帧的优先级,让服务端先处理重要资源,优化用户体验。

缺点:

  • TCP以及TCP+TLS建立连接的延时,HTTP2使用TCP协议来传输的,而如果使用HTTPS的话,还需要TLS协议进行安全传输,而使用TLS也需要一个握手过程,在传输数据之前,导致我们花掉3~4RTT

  • TCP的队头阻塞并没有彻底解决。在HTTP2中,多个请求跑在一个TCP管道中,但当HTTP2出现丢包时,整个TCP都要开始等待重传,那么就会阻塞该TCP连接中的所有请求

谈一谈你对HTTP 2.0 理解

5.2 头部压缩

头部压缩

HTTP 1.1版本会出现User-AgentCookieAcceptServerRange 等字段可能会占用几百甚至几千字节,而 Body却经常只有几十字节,所以导致头部偏重。HTTP 2.0使用HPACK算法进行压缩。

Preview

从上面看,我们可以看到类似于索引表,每个索引表对应一个值,比如索引为2对应头部中的method头部信息,这样子的话,在传输的时候,不在是传输对应的头部信息了,而是传递索引,对于之前出现过的头部信息,只需要把「索引」(比如1,2,...)传给对方即可,对方拿到索引查表就行了。这种传索引的方式,可以说让请求头字段得到极大程度的精简和复用。其次是对于整数和字符串进行哈夫曼编码哈夫曼编码的原理就是先将所有出现的字符建立一张索引表,然后让出现次数多的字符对应的索引尽可能短,传输的时候也是传输这样的「索引序列」,可以达到非常高的压缩率。

5.3 多路复用

多路复用

HTTP 1.x 中,如果想并发多个请求,必须使用多个 TCP 链接,且浏览器为了控制资源,还会对单个域名有 6-8个的TCP链接请求限制。

HTTP2中

  • 同域名下所有通信都在单个连接上完成。
  • 单个连接可以承载任意数量的双向数据流。
  • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装,也就是Stream ID,流标识符,有了它,接收方就能从乱序的二进制帧中选择ID相同的帧,按照顺序组装成请求/响应报文。

5.4 服务器推送

服务器推送

浏览器发送一个请求,服务器主动向浏览器推送与这个请求相关的资源,这样浏览器就不用发起后续请求。相比较http/1.1的优势

  • 推送资源可以由不同页面共享
  • 服务器可以按照优先级推送资源
  • 客户端可以缓存推送的资源
  • 客户端可以拒收推送过来的资源

5.5 二进制分帧

二进制分帧

之前是明文传输,不方便计算机解析,对于回车换行符来说到底是内容还是分隔符,都需要内部状态机去识别,这样子效率低,HTTP/2采用二进制格式,全部传输01串,便于机器解码。这样子一个报文格式就被拆分为一个个二进制帧,用Headers帧存放头部字段Data帧存放请求体数据。这样子的话,就是一堆乱序的二进制帧,它们不存在先后关系,因此不需要排队等待,解决了HTTP队头阻塞问题。

在客户端与服务器之间,双方都可以互相发送二进制帧,这样子双向传输的序列,称为,所以HTTP/2中以流来表示一个TCP连接上进行多个数据帧的通信,这就是多路复用概念。

那乱序的二进制帧,是如何组装成对于的报文呢?

  • 所谓的乱序,值的是不同ID的Stream是乱序的,对于同一个Stream ID的帧是按顺序传输的。
  • 接收方收到二进制帧后,将相同的Stream ID组装成完整的请求报文和响应报文。
  • 二进制帧中有一些字段,控制着优先级和流量控制等功能,这样子的话,就可以设置数据帧的优先级,让服务器处理重要资源,优化用户体验。

六、HTTP 版本-3.0


由于HTTP 2.0依赖于TCP,TCP有什么问题那HTTP2就会有什么问题。最主要的还是队头阻塞,在应用层的问题解决了,可是在TCP协议层的队头阻塞还没有解决。

TCP在丢包的时候会进行重传,前面有一个包没收到,就只能把后面的包放到缓冲区,应用层是无法取数据的,也就是说HTTP2的多路复用并行性对于TCP的丢失恢复机制不管用,因此丢失或重新排序的数据都会导致交互挂掉。为了解决这个问题,Google又发明了QUIC协议,并在2018年11月将QUIC正式改名为HTTP 3.0

  • 优点:

    • 在传输层直接干掉TCP,用UDP替代
    • 实现了一套新的拥塞控制算法,彻底解决TCP中队头阻塞的问题
    • 实现了类似TCP的流量控制、传输可靠性的功能。虽然UDP不提供可靠性的传输,但QUIC在UDP的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些TCP中存在的特性
    • 实现了快速握手功能。由于QUIC是基于UDP的,所以QUIC可以实现使用0-RTT或者1-RTT来建立连接,这意味着QUIC可以用最快的速度来发送和接收数据。
    • 集成了TLS加密功能。目前QUIC使用的是TLS1.3