网络2️⃣HTTP-特性、版本
1、HTTP 特性
以 HTTP/1.1 为例
1.1、优点
- 简单:
- 报文格式
header + body
,首部信息格式key-value
。 - 易于理解,降低了学习和使用门槛。
- 报文格式
- 灵活和易于扩展:
- HTTP 中的请求方法、URI/URL、状态码、首部字段,允许开发人员自定义和扩充。
- HTTP 工作在应用层(
OSI
第七层),其下层可以随意变化。- HTTPS:HTTP 与 TCP 层之间增加 SSL/TLS 安全传输层。
- HTTP/1.1 和 2.0 使用 TCP,而 3.0 改用 UDP。
- 应用广泛和跨平台
1.2、无状态 - Cookie 🔥
无状态:HTTP 不保存通信状态,对于发送过的请求或响应都不做持久化处理。
每当有新的请求发出,都会有新的响应。
- 好处:减少服务器 CPU 和内存消耗,将更多资源用于对外提供服务。
- 坏处:难以完成关联性的操作。
- 假设用户登录->添加购物车->下单->支付,这一系列关联操作都需要知道用户身份。
- 服务器不知道这些请求是有关联的,每次都要问一遍身份信息。
- 解法方案有很多种,比较简单的方式是
Cookie
技术。
Cookie 技术:通过在请求和响应报文中写入 Cookie 信息,来控制客户端的状态。
交互过程如下:
-
客户端:首次访问服务器,检查到本地无 Cookie,直接发起请求。
-
服务端:生成
Cookie
,在响应报文中添加Set-Cookie
首部字段,返回响应。 -
客户端:
-
将
Cookie
信息保存在本地。 -
再次请求访问服务器时,在请求报文中带上 Cookie 值。
-
1.3、不安全 - HTTPS 🔥
HTTP 信息不安全,具体体现为三方面。
- 无加密:采用明文传输。👉可能被窃听
- 无认证:不验证通信方的身份。👉可能遭遇伪装
- 无完整性保护:无法证明报文完整性。👉 可能被篡改
HTTPS 可以解决 HTTP 的三方面不安全,
也就是在 HTTP 和 TCP 之间引入
SSL/TLS
层。
1.4、性能 🔥
HTTP 基于
TCP/IP
,采用请求-应答的通信模式。由此决定了 HTTP 的性能。
1.4.1、长连接
-
短连接(HTTP/1.0):
- 客户端每发起一个 HTTP 请求,都新建一次 TCP 连接(三握)。
- 服务端响应后,关闭 TCP 连接(四挥)。
- 结果:客户端只能串行请求,且增加了双方的通信开销。
-
长连接(HTTP/1.1):aka. 持久连接
-
只要任意一端没有明确提出断连,则保持 TCP 连接状态。
-
如果 HTTP 长连接超过一定时间没有任何数据交互,服务端也会主动断连。
-
结果:减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻服务端的负载。
-
1.4.2、管道网络传输
HTTP/1.1 采用长连接,使管道(pipeline)网络传输成为可能。
aka. 管线化(pipelining)
- 串行:发出请求后,需要得到响应才能发起下一个请求。
- 并行(管线化):无需等待响应,可直接发送下一个请求。
- 好处:减少整体的响应时间。
- 问题:
- 服务器必须按照接收请求的顺序,发送对这些管道化请求的响应。
- 如果服务端在处理某个请求时耗时较长,会阻塞后续请求的处理(i.e. 队头阻塞)
HTTP/1.1 没有默认开启管线化,大部分浏览器也不支持。
HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞(HTTP 层面)。
2、优化 HTTP/1.1
优化思路
- 尽量避免发送 HTTP 请求。
- 减少请求 HTTP 次数。
- 减少服务器的 HTTP 响应的数据大小。
2.1、避免发送请求
策略:缓存
对于一些具有重复性的 HTTP 请求(e.g. 每次请求得到的响应数据相同),
可以将这对请求-响应的数据缓存在本地,下次可直接读取本地数据,无需经过网络请求。
2.2、减少请求次数
减少 HTTP 请求次数,自然就提升了 HTTP 性能。
三种思路:
- 减少重定向请求次数
- 合并请求
- 延迟发送请求
① 减少重定向请求次数
重定向请求:客户端请求的资源不在原有 URL 上,服务器会响应
302
和Location
。客户端需要再对
Location
的 URL 发起请求以获取资源。
-
重定向请求越多,网络中的 HTTP 请求就更多,会降低网络性能。
-
对策:由代理服务器完成重定向的工作。
-
当代理服务器知晓了重定向规则后,可以进一步减少消息传递次数。
② 合并请求
把多个访问小文件的请求合并成一个大的请求。
- 减少了重复发送的 HTTP 首部。
- 减少 TCP 连接数量,避免 TCP 握手和慢启动耗费的时间。
合并方式:将资源合并
参考思路:
- 精灵图:将网页中会使用的多个小图片,使用
CSS Image Sprites
技术合成一个大图片,再根据 CSS 切割还原。 - 打包工具:使用 webpack 等打包工具,将 js、css 等资源打包成一个大文件。
- base64 编码:将图片等二进制数据
base64
编码后,以 URL 形式嵌入到 HTML 文件。
存在问题:
- 当大资源中的某一个小资源发生变化后,客户端必须重新下载整个完整大资源文件。
- 显然带来了额外的网络消耗。
③ 延迟发送请求
策略:按需获取
- 对于 HTML 中的多个 URL,只获取当前需要的资源,而不是一次性获取。
- 从而减少第一时间的 HTTP 请求次数。
2.3、减少响应的数据大小
对策:压缩
对服务器响应的资源进行压缩,从而减少响应的数据大小。
① 无损压缩
无损压缩:资源经过压缩后信息不被破坏,还能完全恢复原样。
适用于文本文件、程序可执行文件、程序源代码。
步骤:
- 针对代码语法规则进行压缩:
- 通常代码文件都有很多换行符或者空格,以提高可读性。
- 机器执行时不需要这些空白符,可先将这部分符号去掉。
- 无损压缩:需要对原始资源建立统计模型,通常用哈夫曼编码算法生成二进制比特序列。
- 常出现的数据:较短的二进制比特序列
- 不常出现的数据、:较长的二进制比特序列
交互流程
-
客户端:在 HTTP 请求报文的首部中,使用
Accept-Encoding
告知自己支持的压缩算法。Accept-Encoding: gzip, deflate, br
-
服务器:从中选择一个支持的/合适的算法,使用该算法对相应资源进行压缩。
Content-Encoding: gzip
② 有损压缩
无损压缩**:解压的数据会与原始数据不同,但是非常接近。
通常用于多媒体数据,如音频、视频、图片。
3、版本 🔥
HTTP/1.1
① 性能提升
相比 HTTP/1.0
- 长连接:改善了 HTTP/1.0 短连接造成的性能开销。
- 管道网络传输:减少整体的响应时间。
② 缺陷
- 报文首部(
Header
):- 未经压缩就发送。首部信息越多延迟越大,导致只能压缩
Body
的部分。 - 冗长。每次互相发送相同的首部造成的浪费较多。
- 未经压缩就发送。首部信息越多延迟越大,导致只能压缩
- 请求/响应:
- 没有请求优先级控制。
- 请求只能从客户端开始,服务器只能被动响应。
- 响应的队头阻塞。
上文中讲述了 HTTP/1.1 的优化思路,但优化程度是有限的。
下面介绍 HTTP/2 和 HTTP/2。
HTTP/2
HTTP/2 基于 HTTPS,安全性有保障。
① 性能提升
头部压缩 - HPACK
如果同时发出多个请求,Header 相同或相似,HTTP/2 会消除重复部分。
即
HPACK
算法。
- 在客户端和服务器同时维护一张 Header 信息表,保存所有字段并生成一个索引号。
- 不发送字段,只发送索引号。
二进制格式
HTTP/1.1
报文是纯文本格式,计算机需要将报文转换为二进制。
HTTP/2
全面采用二进制格式,计算机可以直接解析,提高数据传输效率。
Header
和 Body
都是二进制,统称为帧(Frame
)。
-
头信息帧(Headers Frame)
-
数据帧(Data Frame)
并发传输 - Stream
-
HTTP/1.1
基于请求-响应模型。- 同一个连接中,HTTP 完成一个事务(请求-响应),才能处理下一个事务。
- 如果等待响应的时间过长,客户端无法发送后续请求(队头阻塞)。
-
HTTP/2
引入 Stream 概念。-
多个 Stream 复用同一条 TCP 连接。
-
Stream 可以包含多个 Message(报文),Message 可以包含多个 Frame。
-
使用唯一的
Stream ID
区分 HTTP 请求,则通信双发可以并行交错地发送 Message。
-
服务器推送
服务端不再是被动地响应,可以主动向客户端发送消息。
- 和客户端一样,服务器可以建立 Stream。
- Stream ID 要求:客户端建立的 Stream 是奇数号,服务器建立的 Stream 是偶数号。
服务器推送的好处:可以减少消息传递次数。
示例:客户端请求页面。
- HTTP/1.1:客户端从服务器获取 HTML 文件后,可能需要请求 CSS 等文件。
- HTTP/2:客户端从服务器获取 HTML 文件后,服务器可以直接主动推送 CSS 等文件。
② 缺陷
HTTP/2
通过Stream
解决了 HTTP/1 队头阻塞。但存在另一个问题, TCP 层面的队头阻塞(aka. HTTP/2 队头阻塞)。
原因:HTTP/2 基于 TCP 传输数据,而 TCP 是面向字节流的协议。
- TCP 收到字节数据时会保存到内核缓冲区。
- 如果当前接收到的数据是完整且连续的,Kernel 才会将这部分返回给 HTTP 应用。
- 如果某一个字节数据没有收到,后续接收的数据只能放在内核缓冲区,直到该字节数据到达时才能从 Kernel 中拿到数据。
示例
-
发送方:发送 packet 1-6,其中 packet 3 在网络中丢失了。
-
接收方:
-
Kernel 接收到 packet 1-2,由于是连续的,返回给应用层。
-
Kernel 接收到 packet 4-6,由于中间缺了 packet3,无法返回给应用层。
-
结果:触发 TCP 重传机制。
- 只有等到 packet 3 重传成功后,接收方的应用层才可以从内核中读取到数据。
- i.e. 一个 TCP 连接中的所有的 HTTP 请求,必须等待丢失包的重传。
HTTP/3
① 队头阻塞
HTTP/1.1
和HTTP/2
都存在队头阻塞问题。
-
HTTP/1.1:管道网络传输解决了请求的队头阻塞,没有解决响应的队头阻塞(HTTP 层面)。
-
HTTP/2:Stream 解决了 HTTP 队头阻塞 ,没有解决 TCP 丢包带来的阻塞(TCP 层面)。
HTTP/2
队头阻塞,是因为采用了 TCP 作为传输层协议,因此,
HTTP/3
改用UDP
传输层协议。
-
UDP 是面向无连接的(不保序,不重传),不会导致 HTTP/2 队头阻塞。
-
基于 UDP 的
QUIC
协议,可以实现类似 TCP 的可靠传输。
② QUIC
快速 UDP 互联网连接(Quick Udp Internet Connection)
无队头阻塞
-
QUIC 具有类似 HTTP/2
Stream
与多路复用的概念。- QUIC 可以在同一条连接上并发传输多个 Stream。
- Stream 可以理解为一条 HTTP 请求。
-
QUIC 具有保证传输可靠性的机制,避免队头阻塞。
- HTTP/2:只要发生丢包,所有 Stream 都会阻塞。
- QUIC :Stream 之间互相独立,发生丢包时只阻塞丢包的 Stream,不影响其它 Stream(避免了队头阻塞问题)。
更快的连接建立
-
HTTP/1.x 和 HTTP/2:
- TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层。
- 需要分批次握手(先 TCP 握手,再 TLS 握手),耗时多个 RTT。
-
HTTP/3:
-
QUIC 内部包含 TLS(1.3),它在自己的帧会携带 TLS 里的“记录”。
-
在传输数据前进行 QUIC 协议握手,只需 1RTT 即可完成建立连接与密钥协商。
-
首次连接 & 恢复连接
-
首次连接(1RTT):QUIC 握手信息(连接信息 + TLS 信息)
-
恢复会话(0RTT):应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送。
连接迁移
- HTTP 1.x 和 HTTP 2:
- 基于 TCP,通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。
- 如果 IP 地址变化就需要重新连接(e.g. 从 WiFi 切换到移动数据),TCP 三握+TLS 四握会给用户带来体验上的卡顿。
- HTTP 3:
- 基于 QUIC,通过连接 ID 来标记通信的两个端点。
- 即使 IP 地址变化,只要仍保存上下文信息(连接 ID、TLS 密钥等),就可以复用连接。
总结:QUIC 是一个基于 UDP 的,伪 TCP + TLS + HTTP/2 的多路复用的协议。
现状:QUIC 是新协议,很多网络设备不兼容,会当作 UDP 处理,甚至可能会被直接丢包。