示例说明
连接建立的握手成本在大流量下不可忽视: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(())
}