现在开发者离不开网络,所以都应该了解http协议以及它在整个网络中所起的作用。知道了这些,对开发更好的应用起到很好的帮助作用。

下来,开始讲述HTTP是什么,它的由来,现在的发展以及它是如何走到这一步的。

这是一道填空题http://_________

HTTP是什么?

HTTP(HyperText Transfer Protocol),名为超文本传输协议。之所以叫超文本,是源于当时Tim Beners-Lee博士共享知识的设想,最初的理念是希望通过多文档的互联继而形成超文本,连接可相互参阅的WWW(万维网)。

HTTP是基于TCP/IP的应用层通信协议,它是客户端和服务器之间相互通信的标准。它规定了如何在互联网上请求和传输内容。通过应用层协议,我的意思是,它只是一个规范了主机(客户端和服务器)如何通信的抽象层,并且它本身依赖于TCP/IP来获取客户端和服务器之间的请求和响应。默认的TCP端口是80端口,当然,使用其他端口也是可以的。比如,HTTPS使用的端口是443端口。

HTTP出生

自从在 1989-1991 年间被 CERN(译注:即“欧洲核子研究组织”的原称 - Conseil Européen pour la Recherche Nucléaire)的 Tim Berners-Lee 发明出来以后,HTTP(超文本传输协议) 就一直是万维网的基础传输协议。在 C/S 计算模型中,HTTP 起到了一个“请求/响应”协议的作用。HTTP 标准由 IETF(因特网工程任务组) 和 W3C(万维网联盟)负责开发,并以一系列注释请求(RFCs - Requests for Comments)的形式被发布。HTTP 有 4 个版本:HTTP/0.9、HTTP/1.0、HTTP/1.1 以及 HTTP/2.0。当前通常使用 HTTP/1.1 版本, HTTP/2.0 ,未来是 HTTP/3.0(quic)。

HTTP/0.9 - 极简版(1991)

第一版的HTTP文档是1991年提出来的 HTTP/0.9。这是有史以来最简单的协议;它仅有一个GET方法。如果客户端要访问服务器上的一些网页,它会作出如下的简单请求:

image-20191012183826539

telnet xxx.com 80

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(连接1建立 - TCP 三次握手)
Connected to xxx.xxx.xxx.xxx

(请求)
GET /my-page.html
并且来自服务器的响应内容如下:

(超文本响应)(response body)
<HTML>
A very simple HTML page
</HTML>

(connection closed)

也就是说,服务器会得到这个请求,然后通过HTML格式回复响应内容,且一旦响应内容发送完毕,就会关闭这个连接。归纳一下:

  • 没有header数据块(无法传输其他内容类型的文件), 没有 status/error 代码, 没有 URLs, 没有版本控制
  • GET方法是唯一允许的方法
  • 必须以HTML格式响应,响应类型: 仅 超文本
  • 响应后马上结束的连接

正如你所见,这个协议只不过是未来版本的一个垫脚石罢了。

HTTP/1.0 - 1996

1996年,HTTP的下一个版本,即 HTTP/1.0 诞生了,它在原版本上做出了极大的改善。不像 HTTP/0.9 仅能以HTML格式响应,HTTP/1.1 现在可以处理其他的响应格式了,例如:图像,视频文件,纯文本或其他任何的内容类型。它增加了更多的方法(即 POST 和 HEAD),请求/响应的格式也发生了改变,请求和响应中均加入了HTTP头信息,响应数据还增加了状态码标识,还介绍了字符集的支持、多部分发送、权限、缓存、内容编码等很多内容。

构建可扩展性

  • 对浏览器友好的协议
  • 提供了对请求和响应都包含丰富元数据的 header 域 (HTTP 版本号、status code 和 content type)
  • 响应:不再只限于超文本 (Content-Type 头部提供了传输 HTML 之外文件的能力 — 如脚本、样式或媒体文件)
  • 支持的方法: GET , HEAD , POST
  • 响应后马上结束的连接
  • session/cookie,解决无连接,无状态的请求。

如下所示,这是一个通过 HTTP/1.0 请求和响应的例子:

1
2
3
4
GET / HTTP/1.0
Host: kamranahmed.info
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5)
Accept: */*

正如你所见,客户端除了发送请求以外,它还发送了它的个人信息,要求响应类型等。而在 HTTP/0.9 中因为没有头信息,客户端是不会发送这些信息的。

上面的例子对应的服务器响应结果如下:

1
2
3
4
5
6
7
HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

最开始的响应内容是 HTTP/1.0(HTTP后面的是版本号),然后是状态码200,再往后是原因短语(或状态码的描述)。

在这个新版本中,请求和响应的头信息仍然采用 ASCII 编码方式,但具体的响应内容可以是任何类型的,例如:图像、视频、HTML、纯文本或任何其他的内容类型。因此,现在的服务器端可以向客户端发送任何内容类型的数据;不久之后,HTTP介绍中的“超文本”术语就变得名不副实了。也许用HMTP或超媒体传输协议可能会更有意义一些,但是,我想,我们还是会一直用HTTP这个名字的。

HTTP/1.0的主要缺点之一是,你不能在每个连接中发送多个请求。也就是说,每当客户端要向服务器端请求东西时,它都会打开一个新的TCP连接,并且在这个单独请求完成后,该连接就会被关闭。且对于下个需求时,它必须再创建一个新的连接。为什么会如此糟糕呢?好吧,来让我们做个假设,假设你需要访问一个包含10张图片、5个样式表和5个JS文件的网页,这是一个共20项内容要请求的网页。由于服务器会在每个请求完成后将连接关闭,所以,这将会有一系列的20个独立的连接,每个项目均有一个单独的连接。因为三次握手和其后的缓慢启动机制,若每次请求都创建一个新的TCP连接,这就会带来明显的性能损失,最终的结果就是,这些大量的连接会导致严重的性能下降。

三次握手协议

HTTP/0.9 和 HTTP/1.0都需要为每次请求建立一个新的连接(并在收到对应的响应后立即关闭该连接)。每次新连接建立时,都要经历一遍 TCP 三次握手,并在开始共享应用数据之前会先共享一系列的数据包。

  • SYN - 客户端创建一个随机数(称为x),并且将x发送给服务器。
  • SYN ACK - 服务器确认请求后,当确认x是从客户端发来时会发送一个ACK数据包(称为y,y=x+1)返回给客户端。
  • ACK - 客户端收到从服务器发来的增量y后,向服务器发送ACK(x+1)确认包。

当三次握手完成后,客户端和服务器之间的数据共享就可以开始了。需要注意的是,客户端可能会在处理完最后一个ACK数据包之后,就开始发送应用数据了。但服务器为了完成请求,仍然需要等待ACK数据包收到才行。

image-20191012184239673

问题

然而,一些 HTTP/1.0 的实现试图通过引入一个新的头信息 Connection: keep-alive 来解决这个问题,这是为了告诉服务器:“嘿,服务器,请不要关闭这个连接,我还要用它呢”。但这并没有得到广泛的支持,所以问题仍然存在。

除了是无连接的, HTTP 也是一个无状态的协议,例如:服务器不会维护客户端的信息,因此,在它自己与任何旧的请求没有任何关联的情况下,服务器为了能完成请求,就需要每一个请求都必须带有服务器所需要的信息才行。所以,这简直就是“火上浇油”啊:客户端除了要打开大量的连接,它还必须要发送一些冗余的数据,这就导致了需要使用更多的带宽。

HTTP/1.0+

在发布 HTTP/1.0 之后,网络飞速发展,很多流程web客户端和服务器都在飞快地向HTTP中添加各种特性,以满足快速扩张且在商业上十分成功的万维网的需要。其中很多特性,包括keep-alive连接、虚拟机支持,以及代理连接支持都被加入到HTTP之中,并成为非官方的事实标准。这种非正式的HTTP扩展版本称为 HTTP/1.0+。

HTTP/1.1 - 1999

HTTP/1.0仅发布了3年之后,它的下一个版本,即HTTP/1.1便在1999年问世了,它在之前的基础上做了很多的改进。基于HTTP/1.0的主要改进内容包含:

  • 这是当前普遍使用的 HTTP 版本
  • 进行了重大的性能优化和特性增强,分块传输、压缩/解压、内容缓存磋商、虚拟主机(有单个 IP 地址的主机具有多个域名)、更快的响应,以及通过增加缓存节省了更多的带宽
  • 新增的HTTP方法 PUT、PATCH、HEAD、OPTIONS、DELETE
  • 主机名标识 在 HTTP/1.0 中,Host头信息不是必须项,但 HTTP/1.1 中要求必须要有Host头信息。
  • 持久性连接 正如前面所说,在 HTTP/1.0 中每个连接只有一个请求 ,且在这个请求完成后该连接就会被关闭,从而会导致严重的性能下降及延迟问题。HTTP/1.1 引入了对持久性连接的支持,例如: 默认情况下连接不会被关闭,在多个连续的请求下它会保存连接的打开状态。想要关闭这些连接,需要将 Connection: close 加入到请求的头信息中。客户端通常会在最后一次请求中发送这个头信息用来安全的关闭连接。
  • 管道机制 HTTP/1.1也引入了对管道机制的支持,客户端可以向服务器发送多个请求,而无需等待来自同一连接上的服务器响应,并且当收到请求时服务器必须以相同的顺序来响应。但你可能会问:客户端是怎么知道第一个响应下载完成和下一个响应内容开始的?要解决这个问题,必须要有Content-Length头信息,客户端可以用它来确定响应结束,然后开始等待下一个响应。

http请求过程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
(1 - TCP )
Connected to xxx.xxx.xxx.xxx

(1)
GET /en-US/docs/Glossary/Simple_header HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

(响应1)
HTTP/1.1 200 OK
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Wed, 20 Jul 2016 10:55:30 GMT
Etag: "547fa7e369ef56031dd3bff2ace9fc0832eb251a"
Keep-Alive: timeout=5, max=1000
Last-Modified: Tue, 19 Jul 2016 00:59:33 GMT
Server: Apache
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding

[content]

(请求2)
GET /static/img/header-background.png HTTP/1.1
Host: developer.cdn.mozilla.net
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/en-US/docs/Glossary/Simple_header

(响应2)
HTTP/1.1 200 OK
Age: 9578461
Cache-Control: public, max-age=315360000
Connection: keep-alive
Content-Length: 3077
Content-Type: image/png
Date: Thu, 31 Mar 2016 13:34:46 GMT
Last-Modified: Wed, 21 Oct 2015 18:27:50 GMT
Server: Apache

[image content of 3077 bytes]

(连接1关闭 - 断掉 TCP)

性能优化和特性增强

应该注意的是,为了从持久性连接或管道机制中获益,Content-Length头信息必须在可用的响应中,因为这会让客户端知道当传输完成后,它可以发送下一个请求(用正常顺序发送请求的方式)或者开始等待下一个响应(当启用了管道机制时)。

但这种方法仍然存在一个问题:如果数据是动态的,且服务器找不到之前的内容长度时怎么办?那么,在这种情况下,你就真的不能从持久性连接中收益了,不是吗?!为了解决这个问题,在 HTTP/1.1 中引入了分块编码的支持。在这种情况下,服务器可能会忽略 Content-Length 并支持分块编码。然而,如果他们没有可用的数据,那么连接必须在请求结束时关闭。

  • 分块传输 在动态内容的情况下,当传输开始时服务器无法找到 Content-Length 头信息的话,它也可以开始以块的方式发送内容(一块一块的发),并且当每个小块发送后,它会给每个块添加一个 Content-Length 头信息。当所有的块发送完成后(即整个传输已经完成),它会发送一个空的块(即 Content-Length 为零的块)以确定客户端的传输已经完成。为了通知客户端采用分块传输的方式,服务器需要在头信息中包含Transfer-Encoding: chunked
  • 不像HTTP/1.0 只有基本的身份验证,HTTP/1.1 还包含摘要和代理验证
  • 高速缓存
  • 字节范围
  • 字符集
  • 谈判语言
  • 客户端cookie
  • 加强对压缩的支持
  • 新的状态代码
  • 以及更多。。。

1.0 vs 1.1

TCP 三次握手会在任何连接被建立之前发生一次。最终,当发送了所有数据之后,服务器发送一个消息,表示不会再有更多数据向客户端发送了;则客户端才会关闭连接(断开 TCP)。HTTP/1.0 存在的问题是,对于每个 请求/响应 轮回,也要建立并关闭一个连接。而改用 HTTP/1.1 后的优点则在于,可以复用同一个连接,来完成多次 请求/响应 轮回。

image-20191012184530602

pipeline

image-20191012184600502

特点

  • 管线化机制通过持久连接(persistent connection)完成,仅 HTTP/1.1 支持此技术(HTTP/1.0不支持)
  • 只有 GET 和 HEAD 请求可以进行管线化,而 POST 则有所限制
  • 初次创建连接时不应启动管线机制,因为对方(服务器)不一定支持 HTTP/1.1 版本的协议
  • 管线化不会影响响应到来的顺序,如上面的例子所示,响应返回的顺序并未改变
  • HTTP /1.1 要求服务器端支持管线化,但并不要求服务器端也对响应进行管线化处理,只是要求对于管线化的请求不失败即可
  • 由于上面提到的服务器端问题,开启管线化很可能并不会带来大幅度的性能提升,而且很多服务器端和代理程序对管线化的支持并不好,因此现代浏览器如 Chrome 和 Firefox 默认并未开启管线化支持
  • pipeline中有一个请求被卡住后面的请求都会被阻塞的队头阻塞问题

HTTP Pipelining是这样一种技术:在等待上一个请求响应的同时,发送下一个请求。(译者注:作者这个解释并不完全正确,HTTP Pipelining其实是把多个HTTP请求放到一个TCP连接中一一发送,而在发送过程中不需要等待服务器对前一个请求的响应;只不过,客户端还是要按照发送请求的顺序来接收响应。)但就像在超市收银台或者银行柜台排队时一样,你并不知道前面的顾客是干脆利索的还是会跟收银员/柜员磨蹭到世界末日(译者注:不管怎么说,服务器(即收银员/柜员)是要按照顺序处理请求的,如果前一个请求非常耗时(顾客磨蹭),那么后续请求都会受到影响),这就是所谓的线头阻塞(Head of line blocking)。

image-20191012184655683

即使在2019年的今天,大部分桌面浏览器也默认关闭了HTTP pipelining功能。

不足与缺陷

HTTP/1.1是在1999年发布的,成为标准有很多年了。 虽然它对上一版协议进行了很多改进,但web世界每天都在改变,它开始显现出了它的不足。现在访问的网页与以前相比包含的资源更多。一个简单的网页都会至少打开30个连接。 我们知道 HTTP/1.1 是持久连接,那为什么还需要这么多连接?你会说这是由于HTTP/1.1在任何时刻都只有一个有效连接。 HTTP/1.1尝试通过pipeline来解决这个问题,但是它并没有完全的解决,因为在pipeline中有一个请求被卡住后面的请求都会被阻塞的队头阻塞问题。它将不得不等待下一个请求。 为了克服HTTP/1.1的这些缺点, 开发人员开始尝试一些解决方案,如:在CSS中使用雪碧图、图像编码 ,合并CSS或Javascript文件, 域分片(将多个资源分别放入不同的子域名下)等等。

SPDY - 2009

Google走在前面,它开始试验一种可替换的协议来减少网页的延迟,使得网页加载更快、提升web安全性 。 2009年, 他们称这种协议为SPDY。

SPDY是谷歌的一个商标,不是一个缩写词。

它们意识到如果继续增加带宽来提升网络性能的过程中,必然在到达某一个点后不会再带来更多提升。在有延迟的情况下如果我们不断减少延迟,那么性能将会是一个常数。这是 SPDY性能提升背后的核心理概念,减少延迟来提升网络性能。

对于那些不知道这两个概念的人, 延迟即数据从源传输到目标的耗时(以毫秒为单位),带宽就是每秒传输的数据量(比特/秒).

SPDY的功能包含: 多路复用, 压缩, 优先级, 安全等。我不打算进入SPDY的细节,在进入下一节HTTP / 2后就会明白,HTTP / 2主要受SPDY的启发。

SPDY 没有真正试图替换HTTP,它任然是存在于应用层的基于HTTP的传输层,它在请求发送前进行一些修改。 它开始成为一个事实上的标准,大多数的浏览器开始实现它。

image-20191012184749127

2015年,谷歌不想存在两个相互竞争的标准,因此他们决定把它合并到HTTP中成为HTTP/2,同时放弃SPDY。

image-20191012184805377

HTTP/2 - 2015

现在,你已经知道我们为什么需要一个HTTP协议的修订版了。 HTTP/2 是专为低延迟传输的内容而设计。 关键特征或与 HTTP / 1.1 旧版本的差异,如下:

  • 二进制,而不是文本
  • 多路复用- 在单个连接中多个异步HTTP请求
  • 使用HPACK报头压缩
  • 服务器推送 - 单请求多个响应
  • 请求优先级
  • 安全

image-20191012184830491

特点

  1. 二进制协议 HTTP/2 倾向于使用二进制协议来减少HTTP/1.x中的延迟。二进制协议更容易解析,而不具有像HTTP/1.x 中那样对人的可读性。HTTP/2中的数据块是: 帧和流。

    帧和流 HTTP 消息是由一个或多个帧组成的。有一个叫做 HEADERS 的帧存放元数据,真正的数据是放在 DATA 帧中的,帧类型定义在the HTTP/2 specs(HTTP/2规范),如(HEADERS, DATA, RST_STREAM, SETTINGS, PRIORITY 等)。

    每个HTTP / 2请求和响应都被赋予一个唯一的流ID且放入了帧中。帧就是一块二进制数据。 一系列帧的集合就称为流。 每个帧都有一个流id,用于标识它属于哪一个流,每一个帧都有相同的头。同时,除了流标识是唯一的,值得一提的是,客户端发起的任何请求都使用奇数和服务器的响应是偶数的流id。

    除了 HEADERS和 DATA, 另外一个值得说一说帧类型是RST_STREAM,它是一个特殊的帧类型用于中止流,如:客户端发送这帧来告诉服务器我不再需要这个流了。在 HTTP/1.1 中只有一种方式来实现服务器停止发送响应给客户端,那就是关闭连接引起延迟增加,因为后续的请求就需要打开一个新的连接。 在HTTP/2中,客户端可以使用RST_FRAME来停止接收指定的流而不关闭连接且还可以在此连接中接收其它流。

  2. 多路复用 由于HTTP / 2现在是一个二进制协议,且是使用帧和流来实现请求和响应,一旦TCP 连接打开了, 所有的流都通过这一连接来进行异步的发送而不需要打开额外的连接。反过来,服务器的响应也是异步的方式,如:响应是无序的、客户端使用流id来标识属于流的包。 这就解决了存在于HTTP/1.x 中head-of-line 阻塞问题,如: 客户端将不必耗时等待请求,而其他请求将被处理。

  3. HPACK 头部压缩 它是一个单独的用于明确优化发送header RFC的一部分。它的本质是,当我们同一个客户端不断的访问服务器时,在header中发送很多冗余的数据,有时cookie 就增大header,且消耗带宽和增加了延迟。为了解决这个问题, HTTP/2 引入了头部压缩。

    与请求和响应不同,header不是使用的 gzip 或 compress等压缩格式,它有不同的机制,它使用了霍夫曼编码和在客户端和服务器维护的头部表来消除重复的 headers (如:user agent),在后续的请求中就只使用头部表中引用。

    既然谈到了header,那就再多说一点,它与HTTP/1.1中的一样,不过增加了伪header,如: :method, :scheme,:host 和:path

    image-20191012184948837

  4. 服务器推送 在服务器段,Server push是HTTTP/2的另外一个重要功能,我们知道,客户端是通过请求来获取资源的,它可以通过推送资源给客户端而不需客户端主动请求。 例如,浏览器载入了一个页面,浏览器解析页面时发现了需要从服务器端载入的内容,接着它就发送一个请求来获取这些内容。

    Server push允许服务器推送数据来减少客户端请求。 它是如何实现的呢,服务器在一个新的流中发送一个特殊的帧 PUSH_PROMISE,来通知客户端:“嘿,我要把这个资源发给你!你就不要请求了。”

  5. 请求优先级 客户端可以在一个打开的流中在流的HEADERS 帧中放入优先级信息。 在任何时间,客户端都可以发送一个PRIORITY 的帧来改变流的优先级。

    如果没有优先级信息,服务器就会异步的处理请求,比如:无序处理。如果流被赋予了优先级,它就会基于这个优先级来处理,由服务器决定需要多少资源来处理该请求。

  6. 安全 大家对HTTP/2是否强制使用安全连接(通过TLS)进行了充分的讨论。最后的决定是不强制使用。 然而,大多数厂商表示,他们将只支持基于TLS的 HTTP / 2 。 所以,尽管HTTP / 2规范不需要加密,但它已经成为默认的强制执行的。 在这种情况下,基于TLS实现的 HTTP/2需要的TLS版本最低要求是1.2。 因此必须有最低限度的密钥长度,临时密钥等。

    HTTP/2超越了SPDY的变动也对它进行了增强。HTTP/2有很多性能提升,我们是时候开始使用它。

参考资料

HTTP2 即未来

HTTP2.0原理详细解析

QUIC - 2013

基于SPDY原理,使用UDP快速网络连接。

QUIC是快速UDP网络连线(英语:Quick UDP Internet Connections)的缩写,这是一种实验性的传输层网络传输协议,由Google公司开发,在2013年实现。QUIC使用UDP协议,它在两个端点间创建连线,且支持多路复用连线。在设计之初,QUIC希望能够提供等同于SSL/TLS层级的网络安全保护,减少数据传输及创建连线时的延迟时间,双向控制带宽,以避免网络拥塞。Google希望使用这个协议来取代TCP协议,使网页传输速度加快,计划将QUIC提交至互联网工程任务小组(IETF),让它成为下一代的正式网络规范。

  • QUIC(Quick UDP Internet Connections),直译过来就是“快速的 UDP 互联网连接”,是 Google 基于 UDP 提出的一种改进的通信协议,作为传统 HTTP over TCP 的替代品,开源于 Chromium 项目中。
  • 为了加快 TCP 的传输效率,Google 提出了 BBR 拥塞控制算法,将 TCP 的性能发挥到了极致。由于 TCP 和 UDP 协议是系统内核实现的,要提出新的协议不是不行,只是普及起来会非常困难,就连 BBR 算法,都需要更新系统内核才能支持。那么,TCP 的性能已经到了极致,还能更快吗?
  • UDP 相比于 TCP,没有那么多的要求,只要将数据发出去就行了,不需要考虑数据是否送达了、不需要考虑数据的到达顺序、不需要考虑数据的正确性和完整性,所以效率比 TCP 要高出几个档次。
  • UDP 协议曾经被普遍用于视频直播、网络游戏之类实时性要求较高的应用,即使少数几个包没有送达对应用整体的影响也不大。可是,对于 HTTP 之类的协议,是需要保证数据的正确性、完整性的,所以 UDP 本身并不适合作为 TCP 的替代品。
  • UDP 不适合替代 TCP 是因为它本身不对数据进行校验,那么如果将数据校验放到其他地方去实现,是不是就可以使用 UDP 了呢?
  • 于是,QUIC 就诞生了,它汇集了 TCP 和 UDP 的优点,使用 UDP 来传输数据以加快网络速度,降低延迟,由 QUIC 来保证数据的顺序、完整性和正确性,即使发生了丢包,也由 QUIC 来负责数据的 纠错。

image-20191012185324469

QUIC 的优点

  1. 由于 TCP、UDP 协议是系统内核实现的,更新修改起来并不很方便,而 QUIC 是软件层面实现的,更新迭代起来非常方便。
  2. UDP 本身是无序传输的,这在单个连接上并行传输多个数据有天生的优势:多个数据直接发送即可,由 QUIC 对收到的数据进行重新组合排序,然后送往上层应用。这中间不用等待各种数据确认包,效率非常高。
  3. 在建立 TCP 连接时,需要进行至少三次握手,如果要开启 TLS 加密,则还需要进行 TLS 握手。而 QUIC 采用了类似于 TCP Fast Open 的技术,如果之前连接过,那么之后可以不用重复握手而直接开始传送数据,以实现 0-RTT 往返时延。即便之前没有连接过,也可以在 1-RTT 内完成连接并开始传送数据。并且自身就拥有与 TLS 等效的加密措施。
  4. 在发生丢包时,TCP 会重传丢失的包。而 QUIC,则使用了一种非常神奇的前向纠错算法,通过连续的几个数据包的校验和,可以直接恢复出丢失的包内容,而不需要重传。
  5. 在移动端表现更好:用户的网络环境并不稳定,Wi-Fi、4G、3G、2G 之间来回变化,IP 一旦发生变化,TCP 的连接是不可能保持的。而 QUIC 就不存在这样的问题,通过 ID 来标识用户(而不是 IP + 端口),在连接切换后直接恢复之前的连接会话。
  6. 配合 HTTP/2 API 食用更佳:由于 HTTP/2 采用二进制帧传输机制,QUIC 直接使用这样的机制进行数据传输,效率更高!
  7. 一旦网络异常,会导致TCP出现线头阻塞(Head-of-line blocking),后续数据将被阻塞住;同时针对数据包重传的会导致RTT延时测不太准。人们在使用HTTPS或HTTP/2时,可能因为TLS协议层面一个记录(record)丢失,导致后续其它的记录一样会被阻塞住,出现双重阻塞,这基本上是无解的。TCP丢包阻塞机制导致无法实现真正的多路复用;而QUIC协议层面界定了流与流之间独立的特性,就算是丢包,流与流之间也不会互相影响,实现了真正意义上的多路复用机制。

QUIC 的缺点

现在很多网络运营商会降低 UDP 包的优先级,使得 UDP 丢包率特别高。(QUIC 不可用时,浏览器一般会 Fallback 到 TCP)

目前只有 Chrome、Opera 浏览器支持

什么时候更适合使用 QUIC?

移动端 由于 QUIC 并不使用 IP + 端口来标识客户身份,而是使用 ID,这使得在网络环境切换后还可以保持连接,非常适合用在移动网站上面,在手机信号不稳定的情况下,TCP + TLS 的开销是非常大的!QUIC 的 0-RTT 可以极大限度地提升访问速度。

总结

QUIC 实现的目标,就是利用 UDP 实现一个 TCP,支持 TCP 的所有特性,并且比 TCP 更快更好用。 QUIC 是从 2012 年开始的项目,到目前也还只是草案阶段,并且同样处于草案阶段的 TLS1.3 也同样拥有了 QUIC 中的很多优点(比如 0-RTT)。对于访问速度的优化方式越来越多,适当的选择可以为网站增色许多。

quic落地

QUIC在微博中的落地

腾讯云落地

手机端落地

展望

http 协议发展迅速,和我们现在的技术生活息息相关。5G 的出现也促进了 http 协议的发展。

我们的生活会因为技术的进步,越来越好!