uWSGI部署Django应用时requests.post响应过慢问题排查
首先直接回应你的疑问:确实有可能和进程级资源竞争有关,但更常见的是requests的连接池在uWSGI多进程模型下的冲突,以及同步IO阻塞导致worker耗尽的问题。下面详细拆解原因和解决办法:
核心原因
1. requests连接池在fork后的状态冲突
requests底层依赖urllib3的连接池复用HTTP连接,而uWSGI的master=true+workers=16模式是先启动master进程、初始化部分资源后,再fork出16个worker进程。这会导致所有worker共享同一个连接池的初始状态,当多个worker同时发起请求时,会出现连接池资源竞争、连接状态异常,进而引发请求阻塞、超时。
而manage.py runserver是单进程(或默认多线程)模式,不存在fork后的资源共享问题,所以连接池能正常工作。
2. 同步IO阻塞导致worker耗尽
uWSGI默认是同步worker模式,每个worker同一时间只能处理一个请求。如果你的requests.post调用的外部接口响应较慢,16个worker很快都会被阻塞在等待外部接口返回的状态,后续请求只能排队等待空闲worker,最终表现为整个应用响应极慢。注释掉requests.post后,worker不再被阻塞,自然恢复正常。
针对性解决方案
方案1:重置/禁用requests连接池
避免worker共享连接池,每个请求使用独立的连接或重新初始化连接池:
import requests from requests.adapters import HTTPAdapter def call_external_api(): # 为每个请求创建独立的Session和适配器 session = requests.Session() # 限制连接池大小,或直接禁用复用 adapter = HTTPAdapter(pool_connections=1, pool_maxsize=1) session.mount('http://', adapter) session.mount('https://', adapter) try: # 加上timeout避免无限等待 response = session.post("https://your-external-api.com", timeout=10) response.raise_for_status() return response.json() finally: # 确保关闭会话,释放连接 session.close()
或者更简单的方式,在请求时强制关闭连接:
requests.post("https://your-external-api.com", close=True)
方案2:改用异步请求(推荐)
使用异步HTTP客户端(如aiohttp)配合Django的异步视图,让worker不被阻塞,能同时处理更多请求:
- 先安装依赖:
pip install aiohttp - 修改Django视图为异步模式:
import aiohttp from django.http import JsonResponse async def your_view(request): async with aiohttp.ClientSession() as session: async with session.post( "https://your-external-api.com", timeout=aiohttp.ClientTimeout(total=10) ) as response: data = await response.json() return JsonResponse(data)
- 确保Django配置文件中设置了ASGI应用:
# settings.py ASGI_APPLICATION = "project.asgi.application"
方案3:调整uWSGI配置缓解阻塞
如果暂时无法修改代码,可以调整uWSGI配置增加并发能力:
[uwsgi] env = DJANGO_SETTINGS_MODULE=project.settings harakiri = 1200 max-requests = 5000 daemonize = ./backoffice.log master = true workers=16 # 启用线程,每个worker可处理多个请求 enable-threads=true threads=4 # 启用DNS缓存,避免解析延迟 dns-cache=true vacuum=true
这个方案只是缓解,不能从根本解决阻塞问题,适合临时过渡。
方案4:排查DNS解析问题
有时候uWSGI运行环境的DNS配置和开发环境不同,导致域名解析缓慢。可以尝试直接使用外部接口的IP地址发起请求,或者在uWSGI配置中开启DNS缓存(如上面的dns-cache=true)。
总结
优先尝试方案1或方案2,方案1改动小适合快速验证,方案2是长期优化的方向,能彻底解决同步阻塞带来的worker耗尽问题。
内容的提问来源于stack exchange,提问作者ave

