什么是幂等

一个HTTP方法是幂等的,指的是同样的请求被执行一次与连续执行多次的效果是一样的,服务器的状态也是一样的。简单来说,幂等这个概念指的是多次同样的操作而不改变结果。换句话说就是,幂等方法不应该具有副作用(统计用途除外)。在正确实现的条件下, GET , HEAD , PUT 和 DELETE 等方法都是幂等的,而 POST 方法不是。所有的 safe 方法也都是幂等的。

http 1.0

  • 1.0的HTTP版本,是一种无状态,无连接的应用层协议。 HTTP1.0规定浏览器和服务器保持短暂的链接。
  • 浏览器每次请求都需要与服务器建立一个TCP连接,服务器处理完成以后立即断开TCP连接(无连接),服务器不跟踪也每个客户单,也不记录过去的请求(无状态)。
  • 这种无状态性可以借助cookie/session机制来做身份认证和状态记录。

    无法复用连接

    每次发送请求,都需要进行一次TCP连接,而TCP的连接释放过程又是比较费事的。这种无连接的特性会使得网络的利用率变低。

    队头阻塞(head of line blocking)

    由于HTTP1.0规定下一个请求必须在前一个请求响应到达之前才能发送,假设前一个请求响应一直不到达,那么下一个请求就不发送,后面的请求就阻塞了。

    http 1.1

    1、长连接

  • HTTP1.1增加Connection字段,通过设置Keep-Alive保持HTTP连接不断开。避免每次客户端与服务器请求都要重复建立释放建立TCP连接。提高了网络的利用率。

  • 如果客户端想关闭HTTP连接,可以在请求头中携带Connection:false来告知服务器关闭请求。
  • 如果开启了keep-alive,虽然可以用多次,但是同一时刻只能有一个HTTP请求

    2、队头阻塞

    队头阻塞是指,当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据。针对队头阻塞, 人们尝试过以下办法来解决:

  • 将同一页面的资源分散到不同域名下,提升连接上限。Chrome 有个机制,对于同一个域名,默认允许同时建立 6 个 TCP 持久连接。使用持久连接时,虽然能共用一个 TCP 管道,但是在一个管道中同一时刻只能处理一个请求,在当前的请求没有结束之前,其他的请求只能处于阻塞状态。另外,如果在同一个域名下同时有 10 个请求发生,那么其中 4 个请求会进入排队等待状态,直至进行中的请求完成。

  • Spriting 合并多张小图为一张大图, 再用 JavaScript 或者 CSS 将小图重新“切割”出来的技术。
  • 内联 (Inlining) 是另一种防止发送很多小图请求的技巧,将图片的原始数据嵌入在 CSS 文件里面的 URL 里,减少网络请求次数。
  • HTTP1.1还是无法解决队头阻塞(head of line blocking)的问题

    3、无状态特性 – 带来巨大的 HTTP 头部

    由于报文 Header 一般会携带”User Agent” “Cookie”“Accept””Server”等许多固定的头字段(如下图),多达几百字节甚至上千字节,但 Body 却经常只有几十字节(比如 GET 请求、 204/301/304 响应),成了不折不扣的“大头儿子”。Header 里携带的内容过大,在一定程度上增加了传输成本。更要命的是,成千上万的请求响应报文里有很多字段值都是重复的,非常浪费。

    4、明文传输 – 带来不安全性

    HTTP/1.1 在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。

    5、不支持服务器推送消息

    6、加入了缓存处理(强缓存和协商缓存)

    强缓存
  1. 强缓存不发送请求,直接读取资源,可以获得返回200的状态码
  2. 利用http头中的Expires和Cache-Control两个字段来控制,都用来表示资源的缓存时间,Expires能设置失效时间,而Cache-Control能做到更多选项更细致,如果同时设置的话,其优先级高于Expires

协商缓存

  1. 通过服务器来确定缓存资源是否可用,通过request header判断是否命中请求,命中后返回304状态码,并返回新的request header通知客户端从缓存里取
  2. 普通刷新会启用弱缓存,忽略强缓存。只有在地址栏或收藏夹输入网址、通过链接引用资源等情况下,浏览器才会启用强缓存
  3. 如果时间过期,则向服务器发送header带有If-None-Match和If-Modified-Since的请求,回到1

总结:HTTP/1.1 有两个主要的缺点:安全不足和性能不高

http 2

直到 2009 年,谷歌公开了自行研发的 SPDY 协议,主要解决 HTTP/1.1 效率不高的问题。谷歌推出 SPDY,才算是正式改造 HTTP 协议本身。降低延迟,压缩 header 等等,SPDY 的实践证明了这些优化的效果,也最终带来 HTTP/2 的诞生。
HTTP/2 基于 SPDY,专注于性能,最大的目标是在用户和网站间只用一个连接(connec-tion)
HTTP/2 传输数据量的大幅减少, 主要有两个原因: 以二进制方式传输和 Header 压缩

优点

1、二进制传输

HTTP/2 采用二进制格式传输数据,而非 HTTP/1.x 里纯文本形式的报文 ,二进制协议解析起来更高效。HTTP/2 将请求和响应数据分割为更小的帧,并且它们采用二进制编码

它把 TCP 协议的部分特性挪到了应用层,把原来的”Header+Body”的消息”打散”为数个小片的二进制”帧”(Frame), 用”HEADERS”帧存放头数据、”DATA”帧存放实体数据。HTTP/2 数据分帧后,“Header+Body”的报文结构就完全消失了,协议看到的只是一个个”碎片”。

HTTP/2 中,同域名下所有通信都在单个连接上完成,该连接可以承载任意数量的双向数据流。每个数据流都以消息的形式发送,而消息又由一个或多个帧组成。多个帧之间可以乱序发送,根据帧首部的流标识可以重新组装

2、Header 压缩

HTTP/2 并没有使用传统的压缩算法,而是开发了专门的”HPACK”算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还采用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率。
具体来说:

  • 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键 - 值对,对于相同的数据,不再通过每次请求和响应发送。
  • 首部表在 HTTP/2 的连接存续期内始终存在,由客户端和服务器共同渐进地更新 。
  • 每个新的首部键 - 值对要么被追加到当前表的末尾,要么替换表中之前的值。

    3、多路复用

    在 HTTP/2 中引入了多路复用的技术。多路复用很好地解决了浏览器限制同一个域名下请求数量的问题,同时也更容易实现全速传输

在 HTTP/2 中,有了二进制分帧之后,HTTP /2 不再依赖 TCP 链接去实现多流并行了,在 HTTP/2 中:

  • 同域名下所有通信都在单个连接上完成。
  • 单个连接可以承载任意数量的双向数据流。
  • 数据流以消息的形式发送,而消息又由一个或多个帧组成,多个帧之间可以乱序发送,因为根据帧首部的流标识可以重新组装。

这一特性使性能有了极大提升:

  • 同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应, 这样整个页面资源的下载过程只需要一次慢启动,同时也避免了多个 TCP 连接竞争带宽所带来的问题。
  • 并行交错地发送多个请求 / 响应,请求 / 响应之间互不影响。
  • 在 HTTP/2 中,每个请求都可以带一个 31bit 的优先值,0 表示最高优先级, 数值越大优先级越低。有了这个优先值,客户端和服务器就可以在处理不同的流时采取不同的策略,以最优的方式发送流、消息和帧。

    4、服务端推送

    HTTP2 还在一定程度上改变了传统的“请求 - 应答”工作模式,服务器不再完全被动地响应请求,也可以新建“流”主动向客户端发送消息。比如,在浏览器刚请求 HTML 的时候就提前把可能会用到的 JS、CSS 文件发给客户端,减少等待的延迟,这被称为”服务器推送”

    5、提高安全性

    出于兼容的考虑,HTTP/2 延续了 HTTP/1 的“明文”特点,可以像以前一样使用明文传输数据,不强制使用加密通信,不过格式还是二进制,只是不需要解密。

但由于 HTTPS 已经是大势所趋,而且主流的浏览器 Chrome、Firefox 等都公开宣布只支持加密的 HTTP/2,所以“事实上”的 HTTP/2 是加密的。也就是说,互联网上通常所能见到的 HTTP/2 都是使用”https”协议名,跑在 TLS 上面。HTTP/2 协议定义了两个字符串标识符:“h2”表示加密的 HTTP/2,“h2c”表示明文的 HTTP/2。

缺点

1、如果有丢包请求会等待重传,阻塞后面的数据,有可能不如http1.1的多个TCP连接 TCP 以及 TCP+TLS 建立连接的延时
2、TCP 的队头阻塞并没有彻底解决 TCP 为了保证可靠传输,有一个“超时重传”机制,丢失的包必须等待重传确认
3、多路复用导致服务器压力上升,多路复用没有限制同时请求数。请求的平均数量与往常相同,但实际会有许多请求的短暂爆发,导致瞬时 QPS 暴增
4、多路复用容易 Timeout 大批量的请求同时发送,由于 HTTP2 连接内存在多个并行的流,而网络带宽和服务器资源有限,每个流的资源会被稀释,虽然它们开始时间相差更短,但却都可能超时。

http 3

HTTP/2 的缺点

虽然 HTTP/2 解决了很多之前旧版本的问题,但是它还是存在一个巨大的问题,主要是底层支撑的 TCP 协议造成的。HTTP/2 的缺点主要有以下几点:

TCP 和 TCP+TLS 建立连接的延时

HTTP/2 是使用 TCP 协议来传输的。如果使用 HTTPS 的话,还需要使用 TLS 协议进行安全传输,而使用 TLS 也需要一个握手过程,这样就需要有两个握手延迟过程
①在建立 TCP 连接的时候,需要和服务器进行三次握手来确认连接成功,也就是说需要在消耗完 1.5 个 RTT 之后才能进行数据传输。
②进行 TLS 连接,TLS 有两个版本——TLS1.2 和 TLS1.3,每个版本建立连接所花的时间不同,大致是需要 1~2 个 RTT。
总之,在传输数据之前,我们需要花掉 3~4 个 RTT。

TCP 的队头阻塞并没有彻底解决

上文我们提到在 HTTP/2 中,多个请求是跑在一个 TCP 管道中的。但当出现了丢包时,HTTP/2 的表现反倒不如 HTTP/1 了。因为 TCP 为了保证可靠传输,有个特别的“丢包重传”机制,丢失的包必须要等待重新传输确认,HTTP/2 出现丢包时,整个 TCP 都要开始等待重传,那么就会阻塞该 TCP 连接中的所有请求。而对于 HTTP/1.1 来说,可以开启多个 TCP 连接,出现这种情况反倒只会影响其中一个连接,剩余的 TCP 连接还可以正常传输数据。

QUIC 新功能

QUIC 基于 UDP,而 UDP 是“无连接”的,根本就不需要“握手”和“挥手”,所以就比 TCP 来得快。此外,QUIC 也实现了可靠传输,保证数据一定能够抵达目的地。它还引入了类似 HTTP/2 的“流”和“多路复用”,单个“流”是有序的,可能会因为丢包而阻塞,但其他“流”不会受到影响。具体来说,QUIC 协议有以下特点:

1、实现了类似 TCP 的流量控制、传输可靠性的功能。

虽然 UDP 不提供可靠性的传输,但 QUIC 在 UDP 的基础之上增加了一层来保证数据可靠性传输。它提供了数据包重传、拥塞控制以及其他一些 TCP 中存在的特性。

2、实现了快速握手功能。

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

3、集成了 TLS 加密功能。

目前 QUIC 使用的是 TLS1.3,相较于早期版本 TLS1.3 有更多的优点,其中最重要的一点是减少了握手所花费的 RTT 个数。

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

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

总结

HTTP 1.0

  • 无状态,无连接
  • 短连接:每次发送请求都要重新建立tcp请求,即三次握手,非常浪费性能
  • 无host头域,也就是http请求头里的host,
  • 不允许断点续传,而且不能只传输对象的一部分,要求传输整个对象

    HTTP 1.1

  • 长连接,使用connection:keep-alive使用长连接

  • 请求管道化
  • 增加缓存处理(新的字段如cache-control)
  • 增加Host字段,支持断点传输等
  • 由于长连接会给服务器造成压力

    HTTP 2.0

  • 二进制分帧,更好地安全性

  • 头部压缩,双方各自维护一个header的索引表,使得不需要直接发送值,通过发送key缩减头部大小
  • 多路复用(或连接共享),使用多个stream,每个stream又分帧传输,使得一个tcp连接能够处理多个http请求
  • 服务器推送

    HTTP 3.0

  • 基于google的 QUIC 协议,而 QUIC 协议是使用udp实现的

  • 减少了tcp三次握手时间,以及tls握手时间
  • 解决了http 2.0中前一个stream丢包导致后一个stream被阻塞的问题
  • 优化了重传策略,重传包和原包的编号不同,降低后续重传计算的消耗
  • 连接迁移,不再用tcp四元组确定一个连接,而是用一个64位随机数来确定这个连接
  • 更合适的流量控制