RAG 服务吞吐量低通常是因为同步阻塞了 IO 等待,最推荐的改造方向是将同步 HTTP 请求和数据库查询改为异步非阻塞模式,适用场景为 IO 密集型任务,风险边界在于混合使用同步库会导致事件循环卡顿。
先说结论:异步改造能显著提升 RAG 在高并发下的吞吐量,但前提是链路中所有耗时环节都支持非阻塞调用。
- 先定位:确认耗时主要在 LLM 响应、向量检索还是网络传输。
- 先做:将同步 HTTP 客户端替换为 async 版本,启用框架的异步接口。
- 再验证:通过并发压测对比 QPS 和 P99 延迟,确认无阻塞。
快速处理思路
架构改造没有单行命令,核心是替换阻塞组件。如果当前使用 Flask 或同步 FastAPI 路径,需迁移至 async def 定义的路由;如果 LangChain 链条是同步的,需切换为 ainvoke 或异步流式接口。
为什么会这样
同步代码在等待 LLM 或向量库返回时会独占线程,导致后续请求排队。RAG 流程大部分时间是网络 IO 等待,异步模型允许线程在等待期间处理其他请求,从而提升单位时间内的处理量。
分步处理
第一步,检查当前代码中的阻塞点。搜索代码库中的 time.sleep、requests.post 或同步数据库驱动,这些是必须替换的对象。
第二步,替换 HTTP 客户端。将 requests 库替换为 httpx 的异步客户端,示例配置如下:
import httpx
async with httpx.AsyncClient() as client:
response = await client.post(url, json=data)第三步,启用框架异步支持。FastAPI 路径操作函数使用 async def 定义,确保 LangChain 调用使用 arun 或 astream 方法。
第四步,配置连接池。异步 HTTP 客户端和数据库连接都需要设置合适的连接池大小,避免频繁握手开销。
怎么验证是否生效
使用 locust 或 wrk 进行并发压测,观察 QPS 是否随并发数增加而线性增长。检查服务器日志,确认没有事件循环阻塞警告(如 asyncio DEBUG 日志中的 slow callback)。
常见坑
在异步函数中调用同步库会阻塞整个事件循环,导致所有请求卡顿。向量数据库客户端必须确认支持异步驱动,否则网络请求依然会阻塞线程。
常见问题
异步改造能解决 CPU 瓶颈吗?
不能。异步主要优化 IO 等待,如果是 Embedding 计算或本地模型推理导致的 CPU 满载,需要多进程或 GPU 加速。
必须引入消息队列吗?
不一定。短时请求可直接异步响应,长时任务或需要重试的场景才建议引入 Celery 或 Redis Queue 解耦。