返回文章列表

文章

HTTP/1.1 vs HTTP/2 全面解析

目录
  1. 1. 核心概念
  2. HTTP/1.1
  3. HTTP/2
  4. 2. 工作机制对比
  5. HTTP/1.1 工作模式
  6. HTTP/2 工作模式
  7. 3. 服务端实现对比
  8. HTTP/1.1 服务端 (Node.js)
  9. HTTP/2 服务端 (Node.js)
  10. 4. 客户端实现对比
  11. HTTP/1.1 客户端 (Python)
  12. HTTP/2 客户端 (Python)
  13. 5. 性能差异对比
  14. 延迟表现
  15. 头部大小对比
  16. 6. 使用场景指南
  17. 适合 HTTP/1.1 的场景
  18. 适合 HTTP/2 的场景
  19. 7. 工程实践建议
  20. 迁移到 HTTP/2
  21. 性能优化策略
  22. Spring Cloud中的HTTP选择
  23. 8. 注意事项
  24. HTTP/2 部署注意事项
  25. 兼容性考虑
  26. 9. 总结

1. 核心概念#

HTTP/1.1#

  • 文本协议:请求和响应都是人类可读的文本格式
  • 串行处理:同一连接上的请求必须按顺序处理
  • 队头阻塞:前面的请求处理慢会阻塞后续请求
  • 连接限制:浏览器对同一域名限制6-8个并发连接

HTTP/2#

  • 二进制协议:所有通信使用二进制帧传输
  • 多路复用:单个连接上并行处理多个请求/响应
  • 头部压缩:使用HPACK算法大幅减少头部大小
  • 服务器推送:服务端可主动向客户端推送资源
  • 请求优先级:客户端可指定请求的处理优先级

2. 工作机制对比#

HTTP/1.1 工作模式#

客户端: GET /page1 HTTP/1.1
服务端: HTTP/1.1 200 OK ...
客户端: GET /page2 HTTP/1.1  ← 必须等待page1完成
服务端: HTTP/1.1 200 OK ...

HTTP/2 工作模式#

客户端: [HEADERS帧(流1)] [DATA帧(流2)] [HEADERS帧(流3)] ...
服务端: [DATA帧(流1)] [HEADERS帧(流2)] [DATA帧(流3)] ...

3. 服务端实现对比#

HTTP/1.1 服务端 (Node.js)#

const http = require('http');
const server = http.createServer((req, res) => {
    console.log(`${req.method} ${req.url} - HTTP/1.1`);

    if (req.url === '/') {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.end(`
            <html>
                <head><title>HTTP/1.1</title></head>
                <body>
                    <h1>HTTP/1.1 Page</h1>
                    <link rel="stylesheet" href="/style.css">
                    <script src="/script.js"></script>
                </body>
            </html>
        `);
    } else if (req.url === '/style.css') {
        // 模拟处理延迟
        setTimeout(() => {
            res.writeHead(200, { 'Content-Type': 'text/css' });
            res.end('body { color: blue; }');
        }, 100);
    } else if (req.url === '/script.js') {
        setTimeout(() => {
            res.writeHead(200, { 'Content-Type': 'application/javascript' });
            res.end('console.log("loaded");');
        }, 150);
    }
});

server.listen(3000, () => {
    console.log('HTTP/1.1 server on port 3000');
});

HTTP/2 服务端 (Node.js)#

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

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

server.on('stream', (stream, headers) => {
    const path = headers[':path'];
    console.log(`${headers[':method']} ${path} - HTTP/2`);

    if (path === '/') {
        // 服务器推送关键资源
        stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
            if (!err) {
                pushStream.respond({
                    ':status': 200,
                    'content-type': 'text/css'
                });
                pushStream.end('body { color: blue; }');
            }
        });

        stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
            if (!err) {
                pushStream.respond({
                    ':status': 200,
                    'content-type': 'application/javascript'
                });
                pushStream.end('console.log("loaded");');
            }
        });

        stream.respond({
            ':status': 200,
            'content-type': 'text/html'
        });
        stream.end(`
            <html>
                <head><title>HTTP/2</title></head>
                <body>
                    <h1>HTTP/2 with Server Push</h1>
                    <link rel="stylesheet" href="/style.css">
                    <script src="/script.js"></script>
                </body>
            </html>
        `);
    }
    // 其他资源处理...
});

server.listen(3001, () => {
    console.log('HTTP/2 server on port 3001');
});

4. 客户端实现对比#

HTTP/1.1 客户端 (Python)#

import requests
import time
import threading

def http1_sequential():
    """顺序请求 - 模拟HTTP/1.1的队头阻塞"""
    base_url = "<http://localhost:3000>"
    start_time = time.time()

    responses = [
        requests.get(f"{base_url}/"),
        requests.get(f"{base_url}/style.css"),
        requests.get(f"{base_url}/script.js")
    ]

    for i, resource in enumerate(['HTML', 'CSS', 'JS']):
        print(f"{resource} loaded at {time.time() - start_time:.3f}s")

def http1_parallel():
    """并行请求 - 需要多个TCP连接"""
    base_url = "<http://localhost:3000>"

    def fetch(url, name):
        start = time.time()
        requests.get(url)
        print(f"{name} started at {start:.3f}s")

    threads = []
    resources = [
        (f"{base_url}/style.css", "CSS"),
        (f"{base_url}/script.js", "JS")
    ]

    start_time = time.time()
    for url, name in resources:
        t = threading.Thread(target=fetch, args=(url, name))
        threads.append(t)
        t.start()

    for t in threads:
        t.join()

    print(f"All resources loaded in {time.time() - start_time:.3f}s")

HTTP/2 客户端 (Python)#

import httpx
import asyncio
import time

async def http2_client():
    """HTTP/2 多路复用客户端"""
    async with httpx.AsyncClient(http2=True) as client:
        start_time = time.time()

        # 所有请求在同一个连接上并行发送
        tasks = [
            client.get("<https://localhost:3001/>"),
            client.get("<https://localhost:3001/style.css>"),
            client.get("<https://localhost:3001/script.js>")
        ]

        responses = await asyncio.gather(*tasks)

        for i, (response, name) in enumerate(zip(responses, ['HTML', 'CSS', 'JS'])):
            print(f"{name} loaded at {time.time() - start_time:.3f}s")

        print(f"Using HTTP/{responses[0].http_version}")

# 运行客户端
asyncio.run(http2_client())

5. 性能差异对比#

延迟表现#

场景: 加载包含3个资源的页面

HTTP/1.1 (顺序):
HTML  loaded at 0.105s
CSS   loaded at 0.210s  ← 必须等待HTML
JS    loaded at 0.365s  ← 必须等待CSS

HTTP/1.1 (多连接):
HTML  loaded at 0.105s
CSS   loaded at 0.115s
JS    loaded at 0.120s

HTTP/2 (多路复用):
HTML  loaded at 0.110s
CSS   loaded at 0.110s  ← 同时完成!
JS    loaded at 0.110s  ← 同时完成!

头部大小对比#

# HTTP/1.1 请求头 (~400-800字节)
"""
GET /api/data HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: application/json
Cookie: session=abc123...
Authorization: Bearer xyz...
"""

# HTTP/2 同等请求 (压缩后 ~50-100字节)
# 使用HPACK压缩,重复头部只发送索引

6. 使用场景指南#

适合 HTTP/1.1 的场景#

  • 简单的REST API:请求数量少,资源大
  • 内部系统:网络延迟低,性能要求不高
  • 老系统兼容:需要支持老旧客户端
  • 开发调试:文本协议易于调试

适合 HTTP/2 的场景#

  • 现代Web应用:大量小资源(CSS、JS、图片)
  • 移动应用:高延迟网络环境下优势明显
  • 实时应用:需要低延迟通信
  • 微服务架构:服务间高频通信
  • CDN和代理:需要高效传输

7. 工程实践建议#

迁移到 HTTP/2#

# Nginx 配置示例
server {
    listen 443 ssl http2;  # 启用HTTP/2
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/private.key;

    # HTTP/2优化
    http2_push /style.css;    # 服务器推送
    http2_push /app.js;
}

性能优化策略#

// HTTP/1.1 优化 (现在可能是反模式)
- 资源合并(雪碧图、合并JS/CSS)
- 域名分片(突破连接数限制)
- 减少请求数量

// HTTP/2 优化
- 不要过度合并资源(小文件并行加载更快)
- 利用服务器推送关键资源
- 设置合理的请求优先级
- 启用头部压缩

Spring Cloud中的HTTP选择#

@Configuration
public class HttpConfig {

    // 大多数场景:使用HTTP+REST(开发效率优先)
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    // 性能敏感场景:启用HTTP/2或使用gRPC
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
        return factory -> factory.addConnectorCustomizers(connector -> {
            ProtocolHandler handler = connector.getProtocolHandler();
            if (handler instanceof AbstractHttp11Protocol) {
                ((AbstractHttp11Protocol<?>) handler).setMaxThreads(200);
            }
        });
    }
}

8. 注意事项#

HTTP/2 部署注意事项#

  1. 必须使用HTTPS:主流浏览器要求TLS加密
  2. 服务器配置:需要更新服务器软件支持HTTP/2
  3. 监控调试:二进制协议需要专用工具调试
  4. 推送策略:过度推送可能浪费带宽

兼容性考虑#

// 检测HTTP/2支持
if (window.performance && performance.getEntriesByType) {
    const entries = performance.getEntriesByType('navigation');
    if (entries.length > 0) {
        const protocol = entries[0].nextHopProtocol;
        console.log('Using protocol:', protocol); // http/1.1, h2, etc.
    }
}

9. 总结#

维度HTTP/1.1HTTP/2
协议类型文本二进制
连接管理多连接并行单连接多路复用
头部处理重复发送HPACK压缩
资源加载顺序/队头阻塞并行/无阻塞
服务端能力被动响应主动推送
性能特点高延迟场景差高延迟场景优
适用场景简单API、老系统现代Web、移动应用

工程选择建议

  • 默认选择HTTP/2:新项目都应该使用HTTP/2
  • 渐进式迁移:现有系统可以逐步迁移到HTTP/2
  • 协议不可见:应用代码通常不需要修改,受益于传输层优化
  • 性能测试:在实际网络环境下测试两种协议的表现 HTTP/2在保持HTTP语义不变的前提下,通过底层协议革新大幅提升了性能,是现代Web开发的推荐选择。