异步编程中设置90秒超时却延迟触发ConnectTimeout的原因排查
问题背景
容器运行无限循环,从SQS拉取消息后,用asyncio信号量控制并发数为100,每条消息启动新协程处理。处理中通过httpx.AsyncClient调用外部API,全局超时设为90秒,但实际要等3分钟左右才抛出ConnectTimeout错误。本地对目标API做3500并发压测,超时设为20秒时仍有少量(0-5个)请求出现ConnectTimeout失败。
代码示例
try: self._async_client = AsyncClient(base_url=base_url, timeout=90) logging.info('starting') response = await self._async_client.post(self._SEARCH_ROUTE, json=request.dict(), params=self._get_common_params(account_name, request_id)) logging.info('finished') except httpx.ConnectTimeout as ex: print(f'I get here after 4 minutes')
排查方向
明确超时配置的阶段拆分:httpx的全局超时可能未精准覆盖连接阶段。可以显式拆分连接超时和总请求超时,避免模糊逻辑导致超时失效:
timeout = httpx.Timeout(90, connect=10) # 单独限定连接阶段超时为10秒 self._async_client = AsyncClient(base_url=base_url, timeout=timeout)检查系统级资源限制:容器环境下可能存在文件句柄不足、TCP连接队列长度不够的问题:
- 查看容器的
ulimit设置,确保打开文件数足够支撑并发连接(并发100建议预留200+文件句柄); - 目标API的TCP监听队列(
backlog)过小会导致高并发下连接排队,超过超时时间才抛出错误,本地压测的少量失败也可能与此相关。
- 查看容器的
排查DNS解析延迟:如果目标API的域名解析缓慢,会拉长连接阶段耗时。可以直接用IP调用API排除DNS问题,或在容器内用
dig/nslookup多次测试域名解析耗时。修复连接池复用问题:代码中每次请求都新建
AsyncClient,会导致连接无法复用,高并发下频繁TCP握手会占用资源引发排队超时。应在容器启动时创建一次客户端实例,所有协程共用:# 初始化阶段创建一次客户端 self._async_client = AsyncClient(base_url=base_url, timeout=90) # 处理消息时直接复用 try: logging.info('starting') response = await self._async_client.post(...) logging.info('finished') except httpx.ConnectTimeout as ex: ...排查网络中间件瓶颈:容器所在环境的代理、防火墙或负载均衡,在高并发下可能出现排队或处理延迟,导致连接请求被长时间挂起。可绕过中间件直接测试API连接,验证是否为中间件导致的延迟。
检查目标API负载状态:本地压测时目标API若处于高负载状态,无法及时响应新连接请求会引发超时。查看目标API的CPU、内存、连接数指标,确认压测时是否达到性能瓶颈。
内容的提问来源于stack exchange,提问作者Tomer Vainshtok
相关产品推荐
相关产品推荐

