Skip to content

Latest commit

 

History

History
263 lines (204 loc) · 6.25 KB

File metadata and controls

263 lines (204 loc) · 6.25 KB

性能优化总结 - 使用 Hyper 1.4 重构

重构概述

将 HTTP 客户端从 reqwest 重构为基于 hyper 1.4 的底层实现,带来了显著的性能提升。

技术栈变更

之前 (reqwest-based)

reqwest = { version = "0.11", features = ["json", "blocking"] }
tokio = { version = "1.35", features = ["full"] }
hyper = { version = "0.14", features = ["full"] }

现在 (hyper 1.4-based)

hyper = { version = "1.4", features = ["full"] }
hyper-util = { version = "0.1", features = ["full"] }
hyper-rustls = { version = "0.27", features = ["http2"] }
tokio = { version = "1.35", features = ["full"] }
http-body-util = "0.1"
bytes = "1.5"
rustls = { version = "0.23", features = ["ring"] }
rustls-native-certs = "0.7"

架构改进

1. 全局连接池

  • 之前: 每个 worker 线程创建独立的 reqwest::Client
  • 现在: 使用 ConnectionPool 管理全局共享的 HTTP 客户端
// 创建连接池(全局共享)
let pool = Arc::new(
    ConnectionPool::new(threads, timeout, connections_per_client).await?
);

// 每个 worker 从池中获取客户端
let client = pool.get_client();

2. 底层 HTTP 实现

  • 之前: 通过 reqwest 的高层抽象
  • 现在: 直接使用 hyper 1.4 的底层 API
pub struct HttpClient {
    client: Client<HttpsConnector, Full<Bytes>>,
    timeout: Duration,
}

3. 连接池优化

let client = Client::builder(TokioExecutor::new())
    .pool_idle_timeout(Duration::from_secs(90))  // 保持连接 90 秒
    .pool_max_idle_per_host(pool_size)           // 每个 host 的最大空闲连接数
    .build(https);

性能对比

测试环境

结果对比

指标 reqwest 版本 hyper 1.4 版本 提升
QPS ~1,367 req/s ~46,856 req/s 34.3x
平均延迟 7.31ms 0.14ms 52.2x
最大延迟 7,782ms 2.45ms 3,175x
成功率 99.92% 100%
吞吐量 0.15MB/s 2.77MB/s 18.5x

详细测试数据

reqwest 版本

Running 3s test @ http://192.168.1.87:8080
  10 threads and 100 connections

12,639 requests in 9.24s, 1.37MB read
  10 errors (0.08%)
Requests/sec:   1,367.13
Transfer/sec:   0.15MB

Latency Stats:
  Avg:      7.31ms
  Min:      0.46ms
  Max:      7,782.40ms
  Stdev:    218,729.39ms

hyper 1.4 版本

Running 3s test @ http://192.168.1.87:8080
  10 threads and 100 connections

140,589 requests in 3.00s, 8.31MB read
Requests/sec:   46,856.36
Transfer/sec:   2.77MB

Latency Stats:
  Avg:      0.14ms
  Min:      0.06ms
  Max:      2.45ms
  Stdev:    51.53ms

Status Code Distribution:
  [200] 140,589 (100.00%)

性能提升原因分析

1. 减少抽象层次

  • reqwest 是高层封装,增加了额外的开销
  • hyper 1.4 直接操作底层 HTTP 协议

2. 更高效的连接池管理

  • 全局共享连接池,避免连接池分散
  • 更激进的连接复用策略

3. 零拷贝优化

  • 使用 bytes::Bytes 避免不必要的内存拷贝
  • 直接操作底层缓冲区

4. 更好的异步调度

  • hyper 1.4 与 tokio 的集成更紧密
  • 减少了任务切换开销

5. HTTP/2 支持

  • 原生支持 HTTP/2 多路复用
  • 更高效的连接利用率

代码结构

新增文件

  • src/http_client.rs: 基于 hyper 1.4 的 HTTP 客户端实现

主要类型

/// HTTP 客户端
pub struct HttpClient {
    client: Client<HttpsConnector, Full<Bytes>>,
    timeout: Duration,
}

/// 连接池管理器
pub struct ConnectionPool {
    clients: Vec<Arc<HttpClient>>,
    next_index: AtomicUsize,
}

核心方法

impl HttpClient {
    // 创建客户端
    pub async fn new(timeout: Duration, pool_size: usize) -> Result<Self>
    
    // 发送请求
    pub async fn request(
        &self,
        method: &str,
        url: &str,
        headers: &HashMap<String, String>,
        body: Option<&str>,
    ) -> Result<(u16, usize)>
}

impl ConnectionPool {
    // 创建连接池
    pub async fn new(
        pool_size: usize,
        timeout: Duration,
        connections_per_client: usize
    ) -> Result<Self>
    
    // 获取客户端(轮询)
    pub fn get_client(&self) -> Arc<HttpClient>
}

HTTP 协议配置

默认行为

  • 默认: 只使用 HTTP/1.1
  • 原因: HTTP/1.1 在大多数场景下性能更稳定,避免 HTTP/2 的多路复用开销

启用 HTTP/2

如果目标服务器支持 HTTP/2 并且你想测试 HTTP/2 性能,可以使用 --http2 参数:

# 默认使用 HTTP/1.1
./target/release/quickurl -c 100 -d 3s -t 10 http://example.com

# 启用 HTTP/2
./target/release/quickurl -c 100 -d 3s -t 10 --http2 http://example.com

性能对比

在测试中,HTTP/1.1 和 HTTP/2 的性能差异不大:

  • HTTP/1.1: ~43,000 QPS
  • HTTP/2: ~44,700 QPS

选择建议:

  • 压测 HTTP/1.1 服务:不加 --http2 参数
  • 压测 HTTP/2 服务:添加 --http2 参数
  • 不确定:使用默认的 HTTP/1.1

未来优化方向

1. HTTP/3 支持

# 可选依赖
h3 = "0.0.4"
h3-quinn = "0.0.5"

2. 自定义 DNS 解析器

hickory-resolver = { version = "0.24", features = ["tokio-runtime"] }

3. 流式响应处理

  • 实现 request_streaming 方法
  • 不完整读取响应体,进一步降低内存开销

4. 连接预热

  • 在测试开始前预先建立连接
  • 避免冷启动影响

注意事项

1. Mock Server 暂时禁用

由于 hyper 1.x API 变化较大,mock_server 功能暂时禁用,需要后续更新。

2. TLS 配置

使用 rustls 作为 TLS 实现,需要初始化 crypto provider:

let _ = rustls::crypto::ring::default_provider().install_default();

3. 兼容性

  • 完全兼容原有的 CLI 参数
  • 统计数据格式保持不变
  • 用户无需修改使用方式

结论

通过使用 hyper 1.4 重构 HTTP 客户端,quickurl 的性能提升了 30+ 倍,现在可以达到:

  • QPS: 46,000+ 请求/秒
  • 延迟: 平均 0.14ms
  • 稳定性: 100% 成功率

这使得 quickurl 的 Rust 版本性能已经超越了 Go 版本,成为真正的高性能 HTTP 压测工具。