Django服务处理GET请求时随机挂起,需Ctrl+C恢复
Django应用随机挂起排查求助
问题描述
- 本地网络部署的Django应用接收React请求,页面加载表格(约10个请求)时偶尔随机挂起
- 挂起时请求无法到达视图(视图内的
print语句无输出),视图代码示例:
def f(request): start_time = time.time() print('Received GET request')) ... return JsonResponse(result)
- 用
Ctrl+C中断后,积压的GET请求会一次性输出:
Received GET request <WSGIRequest: GET '/api> Received GET request <WSGIRequest: GET '/api> Received GET request <WSGIRequest: GET '/api> Received GET request <WSGIRequest: GET '/api> Received GET request <WSGIRequest: GET '/api>
- 中断后剩余请求可正常处理;挂起无规律,有时返回大响应会崩溃,有时仅返回Hello World的
/路径也会挂起
环境依赖(Conda)
asgiref 3.7.2 pyhd8ed1ab_0 conda-forge django 4.2.7 pyhd8ed1ab_0 conda-forge django-cors-headers 4.3.1 pyhd8ed1ab_0 conda-forge django-silk 5.0.4 pyhd8ed1ab_0 conda-forge djangorestframework 3.14.0 pyhd8ed1ab_0 conda-forge memory_profiler 0.58.0 pyhd3eb1b0_0 numexpr 2.8.7 py310h2cd9be0_0 pip 23.3.1 py310haa95532_0 pyodbc 4.0.39 py310hd77b12b_0 python 3.10.13 he1021f5_0 sqlite 3.41.2 h2bbff1b_0 sqlparse 0.4.4 py310haa95532_0
- 未使用Django内置SQL模型,自定义了pyodbc SQL类
已尝试的方案
- 尝试Daphne+IIS部署,多线程触发asyncio报错,无法解决:
WARNING Executing <TimerHandle cancelled when=425584.421 AsyncioSelectorReactor._onTimer() created at C:\Users\adm-risk\AppData\Local\anaconda3\envs\risk-django-server\lib\site-packages\twisted\internet\asyncioreactor.py:281> took 0.109 seconds
- 切换到Windows环境下Nginx+Django开发服务器(WSGI)排查
- 用memory_profiler检测内存泄漏,无有效结果(要么无泄漏,要么应用冻结在输出前)
- 用django-silk分析请求,未找到有效线索
排查建议
排查pyodbc连接池/阻塞问题
- 自定义pyodbc类可能存在未正确释放连接、连接池耗尽或同步阻塞的情况:
- 确认每次请求后都关闭数据库连接,或使用线程安全的连接池
- 给数据库操作添加耗时日志,排查是否存在慢查询、锁表导致的阻塞
- 验证Windows下pyodbc的线程兼容性,确保连接在当前线程内创建和使用
- 自定义pyodbc类可能存在未正确释放连接、连接池耗尽或同步阻塞的情况:
替换开发服务器为生产级WSGI服务器
- Django开发服务器默认单线程,高并发下易阻塞。换成Waitress(Windows友好的WSGI服务器):
waitress-serve --port=8000 your_project.wsgi:application - 测试多请求场景,排除开发服务器的单线程瓶颈
- Django开发服务器默认单线程,高并发下易阻塞。换成Waitress(Windows友好的WSGI服务器):
添加细粒度日志追踪
- 在WSGI入口、全局中间件、数据库操作前后添加日志,记录请求路径、线程ID、时间戳:
import logging, threading logger = logging.getLogger(__name__) class RequestLoggingMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): logger.info(f"[{threading.get_ident()}] Request arrived: {request.path}") response = self.get_response(request) logger.info(f"[{threading.get_ident()}] Response sent: {request.path}") return response - 挂起时通过日志定位阻塞环节(WSGI层、中间件、数据库)
- 在WSGI入口、全局中间件、数据库操作前后添加日志,记录请求路径、线程ID、时间戳:
排查Windows系统级问题
- 执行
netstat -ano查看TCP连接状态,排查是否有大量TIME_WAIT连接堆积 - 临时关闭Windows Defender或本地杀毒软件,排除网络请求被拦截
- 挂起时查看任务管理器,确认CPU、内存是否耗尽
- 执行
搭建最小复现环境
- 移除django-silk、django-cors-headers等第三方组件,只保留核心Django和DRF
- 用内存数据替换自定义pyodbc操作,测试是否仍会挂起,逐步定位问题来源
内容的提问来源于stack exchange,提问作者Aditya Gupta
相关产品推荐
相关产品推荐

