返回首页

说说 HTTP/1.0、HTTP/1.1、HTTP/2.0 的区别

问题解析(面试官考察点)

面试官通过此问题主要考察:

  • 对 HTTP 协议演进历史的了解
  • 理解各版本 HTTP 的核心特性
  • 了解 HTTP/2 的多路复用、头部压缩等关键技术
  • 能够分析各版本优缺点及适用场景
  • 了解 HTTP/3 和 QUIC 协议

核心概念(基础知识点)

HTTP 版本演进时间线

1991  HTTP/0.9    只有 GET 方法,纯文本传输
1996  HTTP/1.0    引入 POST/HEAD,增加头部,支持多媒体
1997  HTTP/1.1    持久连接、管道化、分块传输
2015  HTTP/2      二进制分帧、多路复用、头部压缩
2022  HTTP/3      基于 QUIC,UDP 传输

各版本核心特性对比

特性 HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/3
连接方式 短连接 长连接(默认) 多路复用 基于 QUIC
并发请求 串行 管道化(有限) 多路复用 多路复用
传输格式 文本 文本 二进制 二进制
头部压缩 HPACK QPACK
服务器推送 支持 支持
传输层 TCP TCP TCP UDP

详细解答(代码示例)

HTTP/1.0 短连接示例

// 请求 1:建立 TCP 连接 -> 发送请求 -> 接收响应 -> 关闭连接
GET /index.html HTTP/1.0
Host: example.com
Connection: close

HTTP/1.0 200 OK
Content-Type: text/html
Connection: close

<html>...</html>
// TCP 连接关闭

// 请求 2:重新建立 TCP 连接
GET /style.css HTTP/1.0
Host: example.com
Connection: close

// ... 再次关闭连接

问题: 每个资源都需要新建 TCP 连接,三次握手开销大。

HTTP/1.1 长连接与管道化

// 长连接(Keep-Alive)
GET /index.html HTTP/1.1
Host: example.com
Connection: keep-alive

HTTP/1.1 200 OK
Content-Type: text/html
Connection: keep-alive

// 同一连接复用
GET /style.css HTTP/1.1
Host: example.com
Connection: keep-alive

// 连接保持打开状态
// 浏览器并发连接限制
// Chrome 默认对同一域名最多 6 个并发连接

// 域名分片(Domain Sharding)优化
// 将资源分散到多个子域名
const assets = {
  images: 'https://img1.example.com/photo.jpg',
  css: 'https://static.example.com/style.css',
  js: 'https://cdn.example.com/app.js'
};
// 这样可以绕过 6 连接限制,但会增加 DNS 查询和 TCP 连接开销

HTTP/2 多路复用

HTTP/1.1 的队头阻塞问题:
┌──────────────────────────────────────┐
│ 请求1 │ 请求2 │ 请求3 │ 请求4 │ 请求5 │
└──────────────────────────────────────┘
如果请求1响应慢,后面的请求都要等待

HTTP/2 多路复用:
┌──────────────────────────────────────┐
│ 流1: 请求1 ─────────────────────────>│
│ 流3: 请求2 ────────────>             │
│ 流5: 请求3 ────────────────────────> │
│ 流7: 请求4 ──>                       │
│ 流9: 请求5 ─────────────────────>    │
└──────────────────────────────────────┘
多个请求在同一个 TCP 连接上交错传输
// 使用 Node.js http2 模块
const http2 = require('http2');
const fs = require('fs');

const server = http2.createSecureServer({
  key: fs.readFileSync('server.key'),
  cert: fs.readFileSync('server.crt')
});

server.on('stream', (stream, headers) => {
  const path = headers[':path'];

  // 服务器推送示例
  if (path === '/index.html') {
    stream.respondWithFile('index.html');

    // 主动推送 CSS
    stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
      if (err) throw err;
      pushStream.respondWithFile('style.css');
    });
  }
});

server.listen(8443);

HTTP/2 二进制分帧

HTTP/1.1 文本格式:
GET / HTTP/1.1\r\n
Host: example.com\r\n
User-Agent: Mozilla/5.0\r\n\r\n

HTTP/2 二进制帧格式:
┌──────────────────────────────────────────────────────────┐
  Length (24)   Type (8)  Flags (8)  R  Stream ID (31) 
├──────────────────────────────────────────────────────────┤
                      Payload (*)                         
└──────────────────────────────────────────────────────────┘

帧类型:
- HEADERS (0x1): 头部帧
- DATA (0x0): 数据帧
- SETTINGS (0x4): 设置帧
- PRIORITY (0x2): 优先级帧
- RST_STREAM (0x3): 流重置
- PING (0x6): 心跳检测
- GOAWAY (0x7): 连接关闭

头部压缩(HPACK)

// HTTP/1.1 重复头部问题
// 每个请求都发送完整的头部信息
GET /api/users HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123; user=john

GET /api/posts HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Cookie: session=abc123; user=john
// 大部分头部重复!

// HTTP/2 HPACK 压缩
// 使用静态表、动态表和哈夫曼编码
// 重复头部只需发送索引号

深入理解(原理剖析)

HTTP/2 的队头阻塞问题

HTTP/2 解决了应用层队头阻塞,但仍有传输层队头阻塞:

应用层(已解决):
┌─────────────────────────────────────────┐
│  流1  │  流3  │  流5  │  流7  │  流9   │  ← 多路复用
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│  TCP 连接(单个字节流)                   │
└─────────────────────────────────────────┘

传输层(仍存在):
如果 TCP 连接中某个数据包丢失:
┌─────────────────────────────────────────┐
│ 包1 │ 包2 │ 包3 │ ❌丢失 │ 包5 │ 包6   │
└─────────────────────────────────────────┘
                    ↓
所有流都要等待丢包重传(TCP 队头阻塞)

HTTP/3 QUIC 解决方案:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 流1 QUIC│ │ 流3 QUIC│ │ 流5 QUIC│
│ 独立传输 │ │ 独立传输 │ │ 独立传输 │
└─────────┘ └─────────┘ └─────────┘
某个流丢包不影响其他流

HTTP/2 优先级与流量控制

流优先级:
┌─────────────────────────────────────────┐
│              根流 (0)                    │
├─────────────────────────────────────────┤
│    流 A (权重 12)   │   流 B (权重 4)    │
├─────────────────────────────────────────┤
│ 流 C │ 流 D │ 流 E  │      流 F          │
│ (8)  │ (4)  │      │                   │
└─────────────────────────────────────────┘

依赖关系:
-E 依赖流 A
-A 和流 B 同级
- 资源分配比例:A:C:D:E = 12:8:4:0E 依赖 A- B:F = 4:0

流量控制:
- 基于窗口的流量控制
- 每个流独立控制
- 连接级流量控制

HTTP/3 与 QUIC

QUIC 协议栈:
┌─────────────────────────────────────────┐
│  HTTP/3                                 │
├─────────────────────────────────────────┤
│  QUIC (Quick UDP Internet Connections)  │
│  - 加密握手 + 传输握手合并(0-RTT/1-RTT)  │
│  - 内置 TLS 1.3                         │
│  - 连接迁移(Connection Migration)       │
│  - 无队头阻塞的多路复用                   │
├─────────────────────────────────────────┤
│  UDP                                    │
└─────────────────────────────────────────┘

QUIC 优势:
1. 快速握手:通常 0-RTT 或 1-RTT
2. 连接迁移:网络切换(WiFi <-> 4G)保持连接
3. 改进的拥塞控制:可插拔的拥塞控制算法
4. 前向纠错:减少重传延迟

最佳实践

1. HTTP/2 服务器推送策略

// Nginx HTTP/2 服务器推送配置
server {
    listen 443 ssl http2;

    location = /index.html {
        # 推送关键 CSSJS
        http2_push /css/critical.css;
        http2_push /js/app.js;
    }
}

// 注意:不要滥用推送
// - 只推送关键资源
// - 考虑缓存(如果客户端已缓存,推送浪费带宽)
// - 使用 Cookie 或 Service Worker 检测缓存状态

2. 资源优化策略

<!-- HTTP/1.1 优化:合并资源 -->
<!-- 不推荐:增加缓存粒度问题 -->
<link rel="stylesheet" href="all-in-one.css">
<script src="bundle.js"></script>

<!-- HTTP/2 优化:细粒度资源 -->
<!-- 推荐:更好的缓存控制,按需加载 -->
<link rel="stylesheet" href="critical.css">
<script src="core.js" type="module"></script>
<script src="feature-a.js" type="module"></script>

<!-- 使用模块预加载 -->
<link rel="modulepreload" href="feature-a.js">

3. 性能监控

// 使用 Navigation Timing API 监控性能
window.addEventListener('load', () => {
  const timing = performance.timing;

  // DNS 查询时间
  const dnsTime = timing.domainLookupEnd - timing.domainLookupStart;

  // TCP 连接时间
  const tcpTime = timing.connectEnd - timing.connectStart;

  // 首字节时间(TTFB)
  const ttfb = timing.responseStart - timing.requestStart;

  // DOM 解析时间
  const domParse = timing.domComplete - timing.domLoading;

  console.log('Protocol:', performance.getEntriesByType('navigation')[0].nextHopProtocol);
  // 输出: h2 (HTTP/2) 或 h3 (HTTP/3) 或 http/1.1
});

4. 协议协商

# Nginx 协议协商配置
server {
    listen 443 ssl http2;  # 支持 HTTP/2

    # ALPN 协商
    ssl_alpn h2 http/1.1;

    # 如果客户端不支持 HTTP/2,自动回退到 HTTP/1.1
}

面试要点

  1. HTTP/1.1 vs HTTP/2 性能对比

    • HTTP/2 多路复用解决了浏览器 6 连接限制
    • 头部压缩减少带宽消耗(通常减少 30-50%)
    • 服务器推送可以减少 RTT
    • 但实际性能提升受多种因素影响
  2. HTTP/2 的局限性

    • TCP 队头阻塞仍然存在
    • 握手延迟(TCP + TLS)
    • 连接迁移困难(IP 变化需要重连)
  3. HTTP/3 的核心改进

    • 基于 UDP 的 QUIC 协议
    • 0-RTT 连接建立
    • 连接迁移支持
    • 独立的流控制,无队头阻塞
  4. 实际部署考虑

    • TLS 是 HTTP/2 的强制要求
    • 需要确保中间代理支持
    • 服务器推送需要谨慎使用
    • HTTP/3 支持仍在普及中
  5. 面试高频问题

    • 为什么 HTTP/2 使用二进制而非文本?(解析效率、安全性)
    • HTTP/2 的优先级机制如何工作?
    • 什么是 HTTP/2 的流(Stream)?
    • QUIC 相比 TCP 的优势?
    • 如何检测网站使用的 HTTP 版本?(Chrome DevTools Network 面板)