← 中文资源

简体中文版本

Reqwest 连接池与超时配置

调优 reqwest 连接池:每主机空闲连接上限、空闲回收时长、连接与总超时分层设定,连接复用让第二次请求省去握手。

阅读英文原文

示例说明

连接建立的握手成本在大流量下不可忽视:TLS 往返加上 TCP 三次握手几十毫秒起步。连接池让同主机的后续请求直接复用已建立的连接——示例里第二次请求的增量耗时明显低于首次。池参数各有职责:空闲上限防连接无限堆积,空闲超时及时回收陈旧连接,连接超时与总超时分层防不同阶段的挂死。

技术要点

  • pool_max_idle_per_host 控制复用连接数
  • 连接超时与总超时分层防挂
  • 重定向策略显式限定次数

代码解读

build_client 展示完整参数形态(第二个 pool 调用演示参数可被覆盖的写法)。main 里连续两页请求共享同一客户端:首次请求承担建连成本,第二次直接复用池内连接;elapsed 对比直观展示复用收益。查询参数走 query 方法自动编码。

使用建议

每主机上限按下游服务端的连接预算分配。长连接被服务端掐断时 reqwest 会自动重建。CI 环境关掉空闲复用避免泄漏句柄。

代码示例

use std::time::Duration;

use reqwest::Client;

fn build_client() -> reqwest::Result<Client> {
    Client::builder()
        .pool_max_idle_per_host(16)
        .pool_idle_timeout(Duration::from_secs(90))

        .connect_timeout(Duration::from_secs(3))
        .timeout(Duration::from_secs(10))
        .redirect(reqwest::redirect::Policy::limited(3))
        .user_agent("billing-sync/2.1")
        .build()
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::builder()
        .pool_max_idle_per_host(16)
        .pool_idle_timeout(Duration::from_secs(90))
        .connect_timeout(Duration::from_secs(3))
        .timeout(Duration::from_secs(10))
        .build()?;

    let started = std::time::Instant::now();

    // 第一次请求建立连接,后续复用同一池内连接
    let first = client
        .get("https://api.example.com/invoices")
        .query(&[("page", "1")])
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;
    println!("first took {:?}", started.elapsed());

    let second = client
        .get("https://api.example.com/invoices")
        .query(&[("page", "2")])
        .send()
        .await?
        .json::<serde_json::Value>()
        .await?;
    println!("second took {:?}", started.elapsed());

    println!(
        "pages fetched: {} {}",
        first.get("page").and_then(|v| v.as_i64()).unwrap_or(0),
        second.get("page").and_then(|v| v.as_i64()).unwrap_or(0)
    );

    let _ = build_client()?;
    Ok(())
}