如何在FastAPI+Uvicorn的HTTP API中并发提供Matplotlib生成的图片?
解决FastAPI+Uvicorn并发下Matplotlib生成图片异常问题
问题根源
Matplotlib的pyplot(即plt)是全局状态式接口,在Uvicorn的并发请求环境(多线程/多进程)中,多个请求会共享同一个全局plt实例,导致图表状态被互相覆盖、干扰,出现空白图或线程相关错误。即使切换到AGG后端,全局状态的冲突依然存在。
解决方案
1. 提前设置非交互式后端
在导入任何Matplotlib模块之前设置AGG后端,确保后端生效:
import matplotlib matplotlib.use('AGG') # 必须在导入pyplot或其他matplotlib模块前执行 import matplotlib.pyplot as plt from io import BytesIO from PIL import Image from fastapi import FastAPI, Response
2. 改用面向对象的图表创建方式,避免全局状态
每个请求独立创建Figure和Axes实例,使用实例方法而非全局plt接口操作图表,确保请求间状态完全隔离:
app = FastAPI() # 假设SIZE_X和SIZE_Y是你定义的常量 SIZE_X = 10 SIZE_Y = 6 @app.get("/generate-plot") async def generate_plot(): # 每个请求独立创建fig和ax,不依赖全局plt状态 fig, ax = plt.subplots(figsize=(SIZE_X, SIZE_Y)) # 为图表添加元素(示例:绘制折线图) ax.plot([1, 2, 3, 4], [10, 20, 15, 25]) ax.set_title("示例图表") ax.set_xlabel("X轴") ax.set_ylabel("Y轴") # 使用fig.savefig而非plt.savefig,针对当前实例保存 fig_buf = BytesIO() fig.savefig(fig_buf, format='png') fig_buf.seek(0) # 显式关闭当前图表,释放内存并清除状态 plt.close(fig) # 返回图片响应 return Response(content=fig_buf.getvalue(), media_type="image/png")
3. 关键改进点说明
- 避免全局plt操作:用
fig.savefig替代plt.savefig,确保只操作当前请求的图表实例,不会影响其他请求的全局状态。 - 及时清理资源:调用
plt.close(fig)显式关闭当前图表,避免内存泄漏和残留状态干扰后续请求。 - 后端设置时机:
matplotlib.use('AGG')必须在导入plt或其他Matplotlib模块之前执行,否则后端设置不会生效。
额外优化建议
如果服务并发量较高,可以考虑:
- 使用Uvicorn的多进程模式(通过
--workers参数启动),进一步隔离每个请求的进程空间,避免线程间状态共享。 - 对高频请求的图表进行缓存,减少重复生成的开销。
内容的提问来源于stack exchange,提问作者Nicolas
相关产品推荐
相关产品推荐

