使用reqwest crate测试Web API遇Can't assign requested address错误求助
解决reqwest发起大量请求时的"AddrNotAvailable"错误
问题现象
发起一定数量请求后触发panic,错误日志如下:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: reqwest::Error { kind: Request, url: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(8000), path: "/create_my_profile", query: None, fragment: None }, fragment: None } source: hyper::Error(Connect, ConnectError("tcp connect error", Os { code: 49, kind: AddrNotAvailable, message: "Can't assign requested address" })) }
复现环境
- 操作系统:macOS
- 涉事代码:
const BASEURL: &str = "http://127.0.0.1:8000"; const NUM_USRES: usize = 500; pub async fn login(data: &LoginUser) -> Result<Response, reqwest::Error> { let client = reqwest::Client::new(); let url = format!("{}/login", BASEURL); client.post(&url).json(data).send().await } #[tokio::main] async fn main() { for _ in 0..NUM_USERS { let login_user: LoginUser = LoginUser::random_gen(); login(login_user).await; } }
问题根源
- 重复创建Client实例:每次调用
login都新建reqwest::Client,导致每个请求创建独立TCP连接,无法复用连接池,短时间内产生大量新连接。 - TCP端口耗尽:macOS本地可用端口范围有限,且连接关闭后会进入
TIME_WAIT状态(默认保留2分钟),大量新建连接快速耗尽可用端口,触发AddrNotAvailable错误。
解决方案
1. 复用reqwest::Client实例
reqwest::Client内置HTTP连接池,全局创建一次并复用,可避免频繁创建新TCP连接,大幅降低端口占用。
2. 控制并发请求数量
通过信号量(Semaphore)限制同时发起的请求数,避免瞬间占用过多系统资源。
修改后的代码示例
use reqwest::Client; use std::sync::Arc; const BASEURL: &str = "http://127.0.0.1:8000"; const NUM_USERS: usize = 500; const MAX_CONCURRENT: usize = 20; // 根据系统承载能力调整 pub async fn login(client: &Client, data: &LoginUser) -> Result<reqwest::Response, reqwest::Error> { let url = format!("{}/login", BASEURL); client.post(&url).json(data).send().await } #[tokio::main] async fn main() { // 全局初始化Client,复用连接池 let client = Arc::new(Client::new()); // 信号量控制并发请求数 let semaphore = Arc::new(tokio::sync::Semaphore::new(MAX_CONCURRENT)); let mut task_handles = Vec::with_capacity(NUM_USERS); for _ in 0..NUM_USERS { let client_clone = Arc::clone(&client); let permit = semaphore.clone().acquire_owned().await.unwrap(); task_handles.push(tokio::spawn(async move { let login_user = LoginUser::random_gen(); // 处理请求结果,避免unwrap触发panic match login(&client_clone, &login_user).await { Ok(res) => println!("请求成功: {}", res.status()), Err(e) => eprintln!("请求失败: {}", e), } // 释放并发许可,让后续请求执行 drop(permit); })); } // 等待所有任务执行完成 for handle in task_handles { if let Err(e) = handle.await { eprintln!("任务执行异常: {}", e); } } }
可选系统参数调整(不推荐优先操作)
如果上述代码优化后仍存在端口问题,可临时调整macOS TCP参数:
- 缩短
TIME_WAIT时长:sudo sysctl -w net.inet.tcp.msl=1000(默认15000ms) - 扩大本地端口范围:
sudo sysctl -w net.inet.ip.portrange.first=10240
内容的提问来源于stack exchange,提问作者musako
相关产品推荐
相关产品推荐

