凌晨 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,该上多进程就上多进程,别混用。

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

相关推荐
weixin_BYSJ19872 小时前
【计算机毕设】房屋租赁系统02647
java·vue.js·spring boot·python·django·课程设计
在世修行2 小时前
智学文枢:数据归谁管、变了怎么通知
python·桌面应用·状态管理·signal·store·pyside
Misnice2 小时前
Python 的数据类型
java·windows·python
FITA阿泽要努力2 小时前
Sub Agent:上下文管理的艺术
人工智能·python·agent
全栈弄潮儿3 小时前
Python实战第10期:异常处理
后端·python·编程语言
余槐i3 小时前
加了Cookie yt-dlp仍报HTTP 403:3个容易漏的请求头差异
python·http·ffmpeg·开发工具·yt-dlp
菜鸟不秃头3 小时前
手搓 ViT -- 并完成图像分类任务
python
ZhuoAIStage3 小时前
舞台工艺检测表单自动生成:用 Python 与智能体 Skill 打通规范限值到可填表的数据流
python·第三方检测·舞台工艺·表单自动生成·规范性引用
MoRanzhi12034 小时前
第3篇 · 使用 PyQt5 实现 2048 桌面版游戏:MVC 架构与撤销和重做
python·mvc·pyqt5·桌面应用开发·gui开发·2048游戏·撤销与重做