HTTP1.1和HTTP2.0

HTTP报文的格式:
10ff27d1032bf32393195f23ef2f9874

请求行:方法是GET、POST、PUT、DELETE等,URL通过请求DNS获取ip,版本为1.1。

首部:是key-value形式的数据,比如Accept-Charset表示客户端可接收的字符集。Cache-Control表示客户端可接受的缓存时间点,当客户端发送的数据中包含max-age,如果判定缓存层中,资源的缓存时间数值比指定时间的数值小,那么客户端可以接受缓存的资源。

HTTP请求的发送:

HTTP请求由面向连接的TCP组成,通过stream二进制流的方式传给对方,到了TCP层会转换成报文段发送给对方。TCP层每发送一个报文段,都加上自己的IP和目的IP放在IP头里交给IP层传输。IP查看目标IP是否跟自己在同一个局域网,如果是则发送ARP协议获取目标MAC地址,如果不在则需要发送ARP协议获取网关的MAC地址,然后将源MAC和目标MAC放入MAC层发送出去。网关收到包后发现MAC符合,则取出目标IP获取下一跳路由器的MAC,这样一跳一跳的到达目标局域网的路由器,最后一跳的路由器通过ARP获取目标IP的MAC发送出去。目标机器发现MAC符合则收进来,发现IP符合,则根据IP头中协议知道其上一层是TCP,于是解析TCP头查看该序列号是否所需,如果是则放入缓存并发送ACK,如果不是则丢弃。TCP头里有端口号,HTTP服务器正好在监听这个端口号,于是目标机器就把包发给HTTP进程。

HTTP返回的构建:
1c2cfd4326d0dfca652ac8501321fac1-1

服务端在处理完请求需要的资源后,构建一个HTTP包,状态行包括状态码,比如200是一切OK,404是找不到等。首部里面包括Content-Type,表示返回的为JSON还是HTML,还有其他key value形式的内容。构建好返回的报文后,就交给Socket层,交给TCP层去传输,TCP层会将返回的HTML也分为一个个小的段,接下来的路把上面客户端发送到服务端的报文反走一遍。

上面是HTTP1.1的协议内容,下面我们来看HTTP2.0的改进:

HTTP2.0对HTTP头进行一次压缩,对原本携带的大量的key value首部,在两端建立一个索引表,每次都只发送索引就行。

HTTP2.0将一个TCP的连接切分为多个流,每个流有自己的ID,是一个虚拟的通道,可以从客户端发送到服务端,也可以从服务端发送到客户端。

HTTP2.0将所有的传输信息切分为更小的消息和帧,并对他们进行二进制格式编码,常见的帧有Header 帧,用于传输 Header 内容,并且会开启一个新的流。再就是Data 帧,用来传输正文实体。多个 Data 帧属于同一个流。

通过这两种机制,HTTP 2.0 的客户端可以将多个请求分到不同的流中,然后将请求内容拆成帧,进行二进制传输。这些帧可以打散乱序发送, 然后根据每个帧首部的流标识符重新组装,并且可以根据优先级,决定优先处理哪个流的数据。

假设我们的一个页面要发送三个独立的请求,一个获取 css,一个获取 js,一个获取图片 jpg。如果使用 HTTP 1.1 就是串行的,但是如果使用 HTTP 2.0,就可以在一个连接里,客户端和服务端都可以同时发送多个请求或回应,而且不用按照顺序一对一对应。

0bc51f8f887aae04ef89a1a88cb5a17a

HTTP 2.0 其实是将三个请求变成三个流,将数据分成帧,乱序发送到一个 TCP 连接中。

03d4a216c024a9e761ed43c6787bf7dd

QUIC协议

HTTP 2.0 虽然大大增加了并发性,但还是有问题的,因为TCP 协议在处理包时是有严格顺序的。

虽然 HTTP 2.0 通过多个 stream,使得逻辑上一个 TCP 连接上的并行内容,进行多路数据的传输,然而这中间并没有关联的数据。一前一后,前面 stream 2 的帧没有收到,后面 stream 1 的帧也会因此阻塞。

于是我们必须从TCP切换到UDP:

一、自定义连接机制

我们知道TCP的一条连接是基于源 IP、源端口、目的 IP、目的端口四元组标识的,这样在网络不稳定的时候切换都需要重新建立连接。QUIC在自己的逻辑里面维护连接,不再以四元组标识,而是以一个64 位的随机数作为 ID 来标识。因为UDP是无连接的,所以就算四元组变了,只要ID不变就不需要重新建立连接。

二、自定义重传机制

TCP为了保证可靠传输,需要依靠序号和应答机制,以解决顺序问题和丢包问题。如果一个包的ACK超时,则需要重传,超时的时间是根据采样往返时间RTT不断调整的。但是其重传的包的序号是一样的,这时候就有一个问题,如果该序列号收到ACK,我们无法知道这个ACK是原包还是重传包的ACK。QUIC也有个序列号,但是是递增的,任何一个包的序列号只发送一次,QUIC定义了一个offset概念,QUIC发送的数据在这个数据流里面有个偏移量 offset,可以通过offset查看数据发送到了哪里,只要这个offset的包没来,就要重传。最后通过offset把包的数据拼接起来。
da2af1e419db66929dc85107c7250fc4

三、无阻塞的多路复用

一条 QUIC 连接上可以创建多个 stream,发送多个HTTP请求,一个连接上的多个stream无依赖关系,这样即使stream2丢了一个包,但是后面跟着一个stream3的包,那么stream3的包无需等待,可以发给用户。

四、自定义流量控制

QUIC 的流量控制也是通过 window_update,来告诉对端它可以接受的字节数。但是 QUIC 的窗口是适应自己的多路复用机制的,不但在一个连接上控制窗口,还在一个连接中的每个 steam 控制窗口。

QUIC 的 ACK 是基于 offset 的,每个 offset 的包来了,进了缓存,就可以应答,应答后就不会重发,中间的空挡会等待到来或者重发即可,而窗口的起始位置为当前收到的最大 offset,从这个 offset 到当前的 stream 所能容纳的最大缓存,是真正的窗口大小。显然,这样更加准确。

a66563b46906e7708cc69a02d43afb22

comments powered by Disqus