前言

介绍http1.0-3.0的版本优缺点

Http1.0

http1.0之前还有别的版本吗?
答:http0.9

特点:

  • 可以传输任何格式的内容 0.9只能文本
  • 引入了POST和HEAD 0.9只有GET
  • 每次通信都得携带头信息 0.9没有标记信息的手段
  • 增加了响应状态码
  • 缓存的标准是ExpiresIf-Modified-Since

缺点:

  • 不支持断点续传
  • 没有传递主机名hostname

没有解决的问题

服务器发送完响应就关闭了TCP连接。

http连接是在TCP连接里面的,如果这样做,后面再次请求,就要重新建立连接,然鹅我们知道TCP三次握手的成本是比较高的

1.0缓解的方法:

1.0可以使用headers发送信息,其中Connection:keep-alive,就能告诉服务器,客户端后面可能还会请求,先不要关闭TCP连接,从而达到了类似于TCP复用的目的。

为什么说是缓解的方法呢?
因为这个头部字段不是标准的,不同的实现可能导致表现得不一致,因此不能从根本上解决这个问题。

Http1.1

解决了上个版本的问题:

  1. 引入长连接,默认TCP不关闭,可以被多个请求复用
  2. 支持断点续传

特点:

  1. 引入并发连接
  2. 管道机制(一个TCP连接里可以同时发送多个请求,但是响应的顺序和请求的顺序一致,因此不常用)
  3. 增加了PUT、DELETE、OPTIONS、PATCH等方法
  4. 允许响应数据分块chunked,利于传输大文件
  5. 强制要求host头,让主机托管成为可能

缺点:

  1. 队头阻塞(长连接带来)
  2. 无状态
  3. 明文传输
  4. 不支持服务器端推送

队头阻塞的思考

队头阻塞特点:顺序发送的请求因为某些原因被阻塞的时候,后面排队的请求也会一并被阻塞,导致服务端收不到数据。

HTTP1.1为了解决这个问题,有两个方案:

  1. 域名分片+并发连接
  2. 管道机制

域名分片+并发连接

在chrome中,允许一个域名拥有6个TCP持久连接,那么当拥有多级域名的时候,就能将更多的资源分配出来。

但是也会引起一些别的问题,因为TCP的连接需要经过dns,三次握手,慢启动等操作,所以对于服务器来说连接太多容易造成网络拥挤。

管道机制(实际上没有解决)

指的是在一个TCP连接里面,多个HTTP请求可以并行,客户端不用等待上一次请求结果返回,就可以发出下一次请求,但服务端内必须按照接收到客户端请求的先后顺序依次回送响应结果,以保证客户端能区分每次响应的内容

简单来说就是客户端可以一直发,但服务端要按顺序回

一定程度上能解决队头阻塞的问题,但治标不治本,因为如果前面的一个请求很耗费时间,比如大图片的处理,那么后面的即使服务器处理完了也只能等待这个请求处理完才能返回

其他方案

  • 合并小文件减小资源数。精灵图
  • 内联资源。图片放到css的url里面,减少请求次数
  • 减小请求数量。拼接,将多个体积较小的js打包成一个大js,但如果其中一个文件改动就会导致大量数据被重新下载和传输

无状态特性—巨大的HTTP头部

无状态指的是对于连接状态没有记忆能力。纯净的http是没有cookie等机制的,每个连接都是新的连接。

但是由于新增了header标识信息的操作,每次收发都会携带大量的header字段到服务端,这样就逐渐的增加了传输的成本

所以2.0设计的Hpack算法就是为了解决这个问题诞生的。

明文传输—不安全性

1.1在传输数据的时候,所有的内容都是明文的,这样意味着客户端和服务端没办法验证对方的身份,一定程度上无法保证数据的安全性

总结

http1.1因为引入了长连接带来了队头阻塞的问题,使用了域名分片和管道机制缓解,但是没有从根本上解决问题,且引入了header头字段标识信息,但是没有压缩以及存储导致头部信息很大,最后是明文传输的问题,不安全。

SPDY 改进版Http1.1

SPDY:speedy 迅速的。SPDY协议是2012年谷歌提出的方案,优化了http1.x的请求延迟,解决了http的安全性。

  1. 针对http高延迟的问题,speedy采用了多路复用.它通过多个请求stream共享一个tcp连接的方式,解决了http队头阻塞的问题.
  2. 设置了请求优先级,重要的请求优先响应
  3. header压缩
  4. 加密传输
  5. 服务端推送

如上图所示,SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTTP协议(将HTTP1.x的内容封装成一种新的frame格式),同时可以使用已有的SSL功能。

后面的http2.0就是基于speedy协议操作的

Http2

解决了上版本什么问题:

  1. HTTP队头阻塞(流)(多路复用)
  2. 头部过大的问题(头部压缩)

特性:

  1. 二进制分帧
  2. 头部压缩
  3. 多路复用
  4. 服务端推送

二进制传输

HTTP2传输的数据量减少,两个原因,一个是二进制分帧一个是头部压缩。

首先二进制分帧就是采用二进制的形式传输数据,比起1.x的时候采用的纯文本的形式,更容易解析。

详细的来讲,它将Headers+Body的报文形式拆分成一个个二进制的帧,用Headers帧存放头部字段,用Data帧存放请求体数据。

分帧带来的影响就是,服务器接收到的是一堆乱序的二进制帧,所以也就是不存在先后的问题,也就解决了HTTP队头阻塞的问题。(但是TCP队头阻塞还没有解决)

乱序的帧。指的是对于不同ID的流是乱序的,但是同一个ID的流还是顺序的。到达服务端后将顺序的ID组合成完整的报文。

有其他的字段可以实现优先级和流量控制

多路复用

废弃了1.x中的管道。同一个TCP连接里面客户端和服务端可以同时发送多个请求和多个响应,且不用按照顺序来。

头部压缩

HPACK算法,在服务端和客户端之间建立哈希表,用到的字段存放在这张表里面,传输过程中已经出现过的字段就穿索引给对方即可。

对于整数和字符串进行哈夫曼编码,原理是将所有出现的字符建立一张索引表,让出现次数多的字符对于的索引尽可能短,传输的时候也是传输这样的索引序列,可以达到非常高的压缩效率。

服务端推送

另外值得一说的是HTTP2的服务器推送Server Push。在HTTP2中,服务器已经不再是被动的接收请求,响应请求,他也能新建Stream来给客户端发送信息,当TCP连接建立之后,比如浏览器请求一个HTML文件,服务器就可以在返回HTML的基础上,将HTML中引用到的一些其他资源文件一起返回给客户端,减少客户端等待。

2.0缺点

2.0没有解决TCP队头阻塞问题,而TCP仍然是web的构建基础,当TCP传输的数据包在传输过程中丢失的时候,在服务端重新发送丢失的数据包之前,接收方无法确认传入的数据包。由于TCP在设计上不遵循HTTP之类的高级协议,因此单个丢失的数据包将阻塞所有正在进行的HTTP请求中的流,直到重新发送丢失的数据为止。这个问题在不可靠的连接上尤为突出。

多路复用导致服务器压力上升

多路复用没有限制同时请求数,请求的平均数量与往常相同,但实际上会有很多请求短暂爆发,造成瞬时QPS暴增

多路复用容易timeout
大批量的请求同时发送,由于HTTP2连接内存在多个并行的流,而网络请求和服务器资源有限,每个流的资源都会被稀释,虽然它们开始相差更短,但都可能超时

即使是使用NGINX这样的负载均衡器,想正确的进行节流也可能很棘手。 其次,就算你向应用程序引入或调整排队机制,但一次能处理的连接也是有限的。如果对请求进行排队,还要注意在响应超时后丢弃请求,以避免浪费不必要的资源。

HTTP3.0

解决了上个版本的问题:

  1. TCP队头阻塞

面向UDP寻找新可能

HTTP2由于采用二进制分帧进行多路复用,通常只使用一个TCP连接进行传输,丢包或者网络中断的情况下后面所有的数据都会被阻塞。

HTTP/2 的问题不能仅靠应用程序层来解决,因此协议的新迭代必须更新传输层。但是,创建新的传输层协议并非易事。传输协议需要硬件供应商的支持,并且需要大多数网络运营商的部署才能普及。
幸运的是还有另一种选择。UDP 协议与 TCP 一样得到广泛支持,但前者足够简单,可以作为在其之上运行的自定义协议的基础。UDP 数据包是一劳永逸的:没有握手、持久连接或错误校正。HTTP3 背后的主要思想是放弃 TCP,转而使用基于 UDP 的 QUIC (快速UDP互联网连接)协议。

与 HTTP2 在技术上允许未加密的通信不同,QUIC 严格要求加密后才能建立连接。此外,加密不仅适用于 HTTP 负载,还适用于流经连接的所有数据,从而避免了一大堆安全问题。建立持久连接、协商加密协议,甚至发送第一批数据都被合并到 QUIC 中的单个请求/响应周期中,从而大大减少了连接等待时间。如果客户端具有本地缓存的密码参数,则可以通过简化的握手重新建立与已知主机的连接。
为了解决传输级别的线头阻塞问题,通过 QUIC 连接传输的数据被分为一些流。流是持久性 QUIC 连接中短暂、独立的“子连接”。每个流都处理自己的错误纠正和传递保证,但使用连接全局压缩和加密属性。每个客户端发起的 HTTP 请求都在单独的流上运行,因此丢失数据包不会影响其他流/请求的数据传输。

实现了快速握手功能

由于QUIC是基于UDP的,所以QUIC可以实现使用0RTT或1RTT来建立连接,意味着QUIC可以用最快的速度来发送和接收数据,这样可以大大提升首次打开页面的速度。0RTT 建连可以说是 QUIC 相比 HTTP2 最大的性能优势。

集成了TLS加密功能

目前QUIC使用的是TLS1.3,相较于早期版本TLS1.3有更多的优点,其中最重要的一点是减少了握手所花费的RTT个数。
在完全握手情况下,需要 1-RTT 建立连接。 TLS1.3 恢复会话可以直接发送加密后的应用数据,不需要额外的 TLS 握手,也就是 0-RTT。
但是 TLS1.3 也并不完美。TLS 1.3 的 0-RTT 无法保证前向安全性(Forward secrecy)。简单讲就是,如果当攻击者通过某种手段获取到了 Session Ticket Key,那么该攻击者可以解密以前的加密数据。
要缓解该问题可以通过设置使得与 Session Ticket Key 相关的 DH 静态参数在短时间内过期(一般几个小时)。

多路复用,彻底解决TCP中队头阻塞的问题

和TCP不同,QUIC实现了在同一物理连接上可以有多个独立的逻辑数据流(如下图)。实现了数据流的单独传输,就解决了TCP中队头阻塞的问题。

连接迁移

TCP 是按照 4 要素(客户端 IP、端口, 服务器 IP、端口)确定一个连接的。而 QUIC 则是让客户端生成一个 Connection ID (64 位)来区别不同连接。只要 Connection ID 不变,连接就不需要重新建立,即便是客户端的网络发生变化。由于迁移客户端继续使用相同的会话密钥来加密和解密数据包,QUIC 还提供了迁移客户端的自动加密验证。

Http0.9

1991年由W3C制定的标准,诞生之初是为了传输HTML,并且支持GET请求。

当时的请求报文只有一行,GET+请求的路径。服务器在收到请求之后会返回一个以ASCII编码的HTML文档。

缺点:

  1. 没有描述数据的信息,1.0引入了headers
  2. 也只有一个命令
  3. 发送完就关闭TCP连接