凌晨 3 点接口超时,排查发现 asyncio 协程里混用了同步 requests 库

<p style="text-align:center"><img src="https://platform-outputs.agnes-ai.space/images/t2i/task_egT5kpvXJKNPPJvFfr7slkB4hCRbsl97/output_056ce58b9ed045b0a7c4b4697b45b04a.png" alt="封面图" style="max-width:100%;height:auto" /></p>

凌晨 3 点接口超时,排查发现 asyncio 协程里混用了同步 requests 库

凌晨 3 点,报警群炸了。接口 P99 延迟飙到 2 秒,但 CPU 利用率才 20%。第一反应是数据库慢,查了慢查询日志却干干净净。最后定位到 Python 服务里,几个老代码片段的 asyncio 协程里居然直接调了 requests。这种"伪异步"直接阻塞了事件循环,导致所有协程排队等待。单线程模型下,一个阻塞,全员挂起,这就是 CPU 低但响应慢的根本原因。

故障现场还原

出问题的代码长这样,看着是 async 函数,里面却是同步 IO:

复制代码
import asyncio
import requests

async def fetch_data(url):
    # 错误示范:在协程中使用同步阻塞库
    resp = requests.get(url, timeout=5)
    return resp.text

async def main():
    tasks = [fetch_data(f'http://api.example.com/{i}') for i in range(10)]
    await asyncio.gather(*tasks)

asyncio.run(main())

**效果说明**:这段代码执行时,`requests.get` 会阻塞整个线程。虽然写了 async,但 10 个请求是串行执行的,总耗时接近 50 秒,完全失去并发优势,事件循环被卡死。asyncio 核心是单线程事件循环,任何阻塞操作都会阻止后续任务调度。

正确异步姿势

必须使用异步 HTTP 库,如 aiohttp,让出控制权给事件循环:

复制代码
import asyncio
import aiohttp

async def fetch_data(session, url):
    # 正确示范:使用异步 IO 库
    async with session.get(url) as resp:
        return await resp.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_data(session, f'http://api.example.com/{i}') for i in range(10)]
        await asyncio.gather(*tasks)

asyncio.run(main())

**效果说明**:`aiohttp` 在等待 IO 时会挂起协程,切换执行其他任务。10 个请求并发发出,总耗时接近单个请求耗时(约 0.5 秒),吞吐量提升百倍。注意 Session 复用,避免频繁 TCP 握手开销,这是生产环境必备优化。

如何监控阻塞

线上不能全靠肉眼,要开启事件循环慢回调日志,自动捕捉阻塞点:

复制代码
import asyncio
import logging

logging.basicConfig(level=logging.DEBUG)

async def main():
    loop = asyncio.get_running_loop()
    # 设置慢回调阈值为 0.1 秒
    loop.slow_callback_duration = 0.1
    await asyncio.sleep(0.2)  # 模拟耗时操作

asyncio.run(main())

**效果说明**:当协程执行超过 0.1 秒,日志会打印 "Executing callback took longer than 0.1 seconds"。这是定位线上阻塞最直接的证据,比看 CPU 更有用。建议阈值设为 0.05 秒敏感些,配合 ELK 收集日志告警。

老兵检查清单

  1. **严禁同步 IO**:协程内禁止出现 requests, pymysql, time.sleep 等同步库。

  2. **依赖审查**:引入第三方库前,确认是否支持 async/await,不要想当然。

  3. **开启监控**:生产环境务必设置 `slow_callback_duration`,配合日志告警。

  4. **CPU 误导**:异步阻塞时 CPU 往往不高,别被监控指标骗了,要看延迟分布。

  5. **计算密集型**:CPU 密集任务别用 asyncio,该上多进程就上多进程,别混用。

你们遇到过协程阻塞最难查的案例是啥?

相关推荐
databook29 分钟前
新数据到手,先跑哪几步检查
python·数据分析
czq_26867194871 小时前
Python打卡第46天
开发语言·python
Flynt2 小时前
日涨4000星的OSINT工具,我拿Linus的用户名和一个随机字符串实测了一遍
python·安全
曲径海盗5 小时前
【Python 学习第 53 天】列表基础
python·学习笔记·编程入门
Dumbness5 小时前
群里 @ 机器人发"查订单",指令一条都没命中:微信群机器人在路由前漏掉的几步归一化
python·微信
用户1614840955455 小时前
用 M4 Pro 跑羽毛球视频 AI 分析(二):TrackNet MPS 加速 3.2 倍、卡尔曼滤波补漏与自动回合剪辑
python
用户4019925568616 小时前
基于对话树回溯的社媒评论流买方意图识别与噪音过滤实践
python
用户0332126663676 小时前
使用 Python 加密或解密 PDF 文档
python
小七-七牛开发者6 小时前
让 Go 走得更远:用 LLGo 打通 Go 与 C/C++/Python 生态
python·编程·编译器·llvm·llgo
CyrusYiVPP7 小时前
实测 Ollama 本地部署:我用 Python 搓了个离线问答机器人,Qwen4 27B 一到换个模型名就能用
人工智能·python