MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

HTTP/2协议的多路复用与头部压缩技术

2021-02-165.6k 阅读

HTTP/2协议的多路复用技术

1. 传统HTTP协议的连接限制

在HTTP/1.1协议中,浏览器与服务器之间建立的TCP连接存在着一些限制。浏览器为了提高页面资源的加载速度,通常会同时发起多个HTTP请求来获取不同的资源,比如HTML文件、CSS样式表、JavaScript脚本、图片等。然而,每个浏览器对同一个域名下的并发连接数是有限制的,常见的限制数量在2到6个之间。这意味着,如果页面中有大量的资源需要加载,就需要排队等待连接可用,这会显著影响页面的加载性能。

例如,假设一个网页需要加载10张图片、1个CSS文件和2个JavaScript文件,而浏览器对同一个域名的并发连接数限制为6。那么一开始会有6个请求同时发送,但剩下的7个请求就需要等待前面6个请求中的部分完成后,才能使用释放出来的连接进行发送。这种连接限制导致了资源加载的串行化,延长了页面的整体加载时间。

2. HTTP/2多路复用的原理

HTTP/2协议引入了多路复用技术,彻底解决了上述连接限制的问题。多路复用允许在同一个TCP连接上同时发送多个请求和接收多个响应,并且这些请求和响应之间不会相互阻塞。

在HTTP/2中,每个请求和响应都被封装在一个独立的帧(Frame)中。这些帧通过一个共享的TCP连接进行传输,并且每个帧都有一个唯一的流标识符(Stream ID),用于标识该帧所属的请求或响应流。通过这种方式,多个请求和响应可以在同一个TCP连接上交错传输,而不会相互干扰。

例如,浏览器同时发起三个请求,分别请求图片A、图片B和CSS文件。在HTTP/2协议下,这三个请求的帧会被依次放入TCP连接的发送队列中,按照一定的顺序发送到服务器。服务器接收到这些帧后,根据流标识符将它们分发给对应的处理程序进行处理。处理完成后,服务器同样通过TCP连接将响应帧发送回浏览器,浏览器再根据流标识符将响应帧组装成完整的响应。

3. 多路复用的优势

  • 提高页面加载速度:由于多个请求可以同时在一个连接上进行,无需等待前一个请求完成才能发送下一个请求,大大减少了资源加载的等待时间,从而提高了页面的整体加载速度。
  • 减少连接开销:传统HTTP/1.1中,每个请求都可能需要建立一个新的TCP连接,而建立TCP连接需要进行三次握手等操作,这会带来一定的开销。HTTP/2的多路复用技术通过在一个连接上传输多个请求,减少了连接的建立次数,降低了连接开销。
  • 更好的资源利用:共享TCP连接使得网络资源得到更充分的利用,避免了因为连接限制而导致的资源闲置。

4. 代码示例(Node.js)

以下是一个简单的Node.js示例,展示如何使用http2模块来创建一个支持HTTP/2多路复用的服务器:

const http2 = require('http2');
const fs = require('fs');

const server = http2.createServer({
  key: fs.readFileSync('localhost-privkey.pem'),
  cert: fs.readFileSync('localhost-cert.pem')
});

server.on('stream', (stream, headers) => {
  // stream是一个Duplex流,可以用于读写数据
  stream.respond({
    'content-type': 'text/plain',
    ':status': 200
  });
  stream.end('Hello, HTTP/2!');
});

server.listen(8443, () => {
  console.log('Server listening on port 8443');
});

在这个示例中,我们使用http2.createServer创建了一个HTTP/2服务器,并配置了SSL证书(因为HTTP/2通常运行在HTTPS之上)。当有新的流(即请求)到达时,我们通过stream.respond方法发送响应头,然后通过stream.end方法发送响应体。这样,多个请求可以在同一个TCP连接上并发处理,体现了HTTP/2的多路复用特性。

HTTP/2协议的头部压缩技术

1. HTTP头部的问题

在传统的HTTP协议中,每次请求和响应都会携带大量的头部信息。这些头部信息包含了诸如请求方法、URL、主机名、用户代理、Cookie等各种元数据。随着Web应用的复杂性增加,头部信息的大小也在不断增长。

例如,一个简单的HTTP请求头部可能如下:

GET /index.html HTTP/1.1
Host: example.com
User - Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q = 0.9,image/avif,image/webp,image/apng,*/*;q = 0.8,application/signed - exchange;v = b3;q = 0.9
Accept - Encoding: gzip, deflate, br
Accept - Language: en - US,en;q = 0.9
Cookie: session_id = 1234567890; username = JohnDoe

这样的头部信息在每次请求时都会被发送,即使其中的很多字段在多次请求中是相同的。这不仅增加了数据传输的量,也浪费了带宽和服务器资源。

2. HTTP/2头部压缩的原理

HTTP/2协议采用了HPACK(HTTP/2 Header Compression)算法来对头部进行压缩。HPACK算法主要基于以下几种机制:

  • 静态字典:HPACK定义了一个静态字典,包含了一些常见的HTTP头部字段及其值。例如,:method: GET:scheme: http等。当头部中的某个字段和值能够在静态字典中找到匹配时,就可以通过引用字典中的索引来代替实际的字段和值,从而减少传输的数据量。
  • 动态字典:除了静态字典,HPACK还维护一个动态字典。在通信过程中,新出现的头部字段和值会被添加到动态字典中。后续如果再次出现相同的头部字段和值,就可以通过引用动态字典中的索引来传输。动态字典的大小是有限的,当字典满了之后,会按照一定的规则淘汰旧的条目。
  • 哈夫曼编码:对于无法在字典中找到匹配的头部字段和值,HPACK会使用哈夫曼编码进行进一步压缩。哈夫曼编码是一种基于统计概率的编码方式,它将出现频率较高的字符用较短的编码表示,出现频率较低的字符用较长的编码表示,从而达到压缩数据的目的。

3. 头部压缩的优势

  • 减少带宽消耗:通过对头部信息的压缩,大大减少了每次请求和响应中头部数据的传输量,从而节省了带宽,特别是在移动网络等带宽受限的环境中,效果更为显著。
  • 提高传输效率:减少了头部数据的传输量,也就意味着更快的传输速度,提高了整个HTTP通信的效率。
  • 降低服务器负载:由于减少了传输的数据量,服务器处理请求和响应的压力也相应降低,能够处理更多的并发请求。

4. 代码示例(Go语言)

以下是一个简单的Go语言示例,展示如何使用http2库来实现HTTP/2服务器,并观察头部压缩的效果:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content - Type", "text/plain")
        fmt.Fprintf(w, "Hello, HTTP/2!")
    })

    log.Fatal(http.ListenAndServeTLS(":8443", "localhost - cert.pem", "localhost - privkey.pem", nil))
}

在这个示例中,我们使用Go语言的http包创建了一个HTTP/2服务器(通过ListenAndServeTLS函数,表明使用HTTPS协议,HTTP/2通常运行在HTTPS之上)。虽然代码中没有直接展示头部压缩的操作,但当客户端与这个服务器进行通信时,HTTP/2协议会自动对头部进行压缩。可以通过抓包工具(如Wireshark)来观察实际传输的头部数据,与传统HTTP协议相比,会发现头部数据量明显减少,体现了头部压缩的效果。

多路复用与头部压缩技术的结合

1. 协同工作提升性能

HTTP/2协议的多路复用和头部压缩技术并不是孤立存在的,它们相互配合,共同提升了HTTP通信的性能。

多路复用允许在一个TCP连接上同时传输多个请求和响应,而头部压缩则减少了每个请求和响应中头部数据的大小。这意味着在相同的带宽条件下,可以更快速地传输更多的请求和响应。

例如,假设一个页面需要加载多个资源,每个资源的请求和响应都包含一定大小的头部信息。在HTTP/1.1协议下,由于连接限制和头部未压缩,页面加载时间可能较长。而在HTTP/2协议下,多路复用使得这些请求可以并发传输,头部压缩又减少了每个请求和响应的头部数据量,大大加快了页面资源的加载速度。

2. 对服务器和客户端的影响

  • 服务器端:服务器需要支持HTTP/2协议的实现,包括多路复用和头部压缩的处理。这可能需要对现有的服务器软件进行升级或重新开发。在处理大量并发请求时,由于多路复用和头部压缩减少了数据传输量,服务器的负载压力会有所降低,但同时也需要处理更复杂的协议逻辑。
  • 客户端:客户端同样需要支持HTTP/2协议。现代的浏览器大多已经支持HTTP/2,但一些老旧的客户端可能不支持。对于支持HTTP/2的客户端,在与服务器通信时可以享受到多路复用和头部压缩带来的性能提升,如更快的页面加载速度、更低的流量消耗等。

3. 实际应用中的考虑

在实际应用中,虽然HTTP/2的多路复用和头部压缩技术带来了显著的性能提升,但也需要考虑一些兼容性和配置问题。

  • 兼容性:要确保服务器和客户端都支持HTTP/2协议。对于不支持HTTP/2的客户端,服务器可能需要降级到HTTP/1.1协议进行通信。
  • 配置优化:服务器需要合理配置HTTP/2相关的参数,如动态字典的大小、连接的并发数等,以达到最佳的性能。同时,客户端也可以通过一些设置来优化与HTTP/2服务器的交互。

与HTTP/3的对比

1. HTTP/3的新特性

HTTP/3是HTTP协议的最新版本,它在HTTP/2的基础上进行了进一步的改进。HTTP/3基于UDP协议,使用QUIC(Quick UDP Internet Connections)协议来实现可靠传输。

与HTTP/2相比,HTTP/3主要有以下新特性:

  • 基于UDP的传输:UDP协议相比于TCP协议,具有更低的延迟和更高的传输效率。HTTP/3通过QUIC协议在UDP上实现了可靠的传输,减少了连接建立的延迟,特别是在高延迟或网络不稳定的环境中表现更优。
  • 更好的连接迁移:在HTTP/3中,由于每个连接都有一个唯一的连接标识符,当设备的网络环境发生变化(如从Wi-Fi切换到移动数据)时,连接可以更容易地迁移到新的网络,而不会中断正在进行的传输。

2. 多路复用和头部压缩的继承与改进

HTTP/3继承了HTTP/2的多路复用和头部压缩技术,并在某些方面进行了改进。

在多路复用方面,由于基于UDP的QUIC协议,HTTP/3在处理并发流时可以更加灵活,进一步提高了多路复用的性能。例如,QUIC协议可以更好地处理流的优先级和拥塞控制,使得重要的流可以优先得到传输。

在头部压缩方面,HTTP/3采用了QPACK(QUIC - based HTTP/3 Header Compression)算法,它在HPACK的基础上进行了优化。QPACK通过更高效的字典管理和编码方式,进一步减少了头部压缩的开销,提高了压缩效率。

3. 应用场景和未来发展

HTTP/3适用于那些对延迟和网络稳定性要求较高的场景,如实时通信、在线游戏等。随着网络技术的不断发展,HTTP/3有望在更多的领域得到应用。

然而,由于HTTP/3是一个相对较新的协议,目前的普及程度还不如HTTP/2。在未来的发展中,需要更多的服务器和客户端支持HTTP/3,同时也需要进一步优化协议的性能和兼容性,以推动其广泛应用。

总结

HTTP/2协议的多路复用和头部压缩技术是对传统HTTP协议的重大改进。多路复用解决了连接限制的问题,提高了页面加载速度和资源利用效率;头部压缩则减少了头部数据的传输量,节省了带宽并提高了传输效率。这两种技术相互配合,为现代Web应用的高性能通信提供了坚实的基础。

虽然HTTP/3在某些方面对HTTP/2进行了进一步的改进,但HTTP/2仍然是目前广泛应用的协议版本。对于后端开发人员来说,深入理解和掌握HTTP/2的多路复用和头部压缩技术,对于优化Web应用的性能、提升用户体验具有重要意义。在实际开发中,应根据应用的需求和目标用户的网络环境,合理配置和使用HTTP/2协议,以充分发挥其性能优势。同时,也需要关注HTTP/3等协议的发展,为未来的技术升级做好准备。