Django应用性能优化:阻塞与非阻塞实现问题排查
Django应用AB负载测试问题排查与优化
问题描述
使用Apache Benchmark(ab)对Django应用做负载测试时,并发提升至50出现错误:apr_socket_recv: Connection reset by peer (54);且20并发下,基于ASGI+Daphne的异步实现吞吐量与WSGI阻塞实现持平,未达预期性能提升。
应用包含两种实现:
- 阻塞方案:基于默认WSGI开发服务器,使用requests库发起同步请求调用外部API
- 异步方案:基于ASGI+Daphne服务器,在异步视图中使用aiohttp调用外部API
两种实现均需向外部API端点发送POST请求并返回响应。
负载测试命令:
ab -c 50 -n 600 -s 800007 -T application/json "http://127.0.0.1:8001/test"
阻塞实现代码
from rest_framework.decorators import api_view import requests as requests from rest_framework.response import Response @api_view(['GET']) def index(request): res = make_api_request("http://{host}/v1/completions") print("blocking response is ---->", res) return Response(res, status=200) def make_api_request(url, method="POST", headers=None, params=None, json_data=None, timeout=None): try: json_data = {'prompt': 'Hi, How are you?', 'max_new_tokens': 700, 'temperature': 0.1, 'top_p': 1, 'max_tokens': 700, 'model': 'meta-llama/Llama-2-7b-chat-hf'} response = requests.request(method, url, headers=headers, params=params, json=json_data, timeout=timeout) return response except requests.exceptions.Timeout as e: raise TimeoutError(f"Request timed out. The server did not respond within the specified timeout period.") except requests.exceptions.RequestException as e: raise ConnectionError(f"Request error: {str(e)}") except Exception as e: raise Exception(f"Exception error: {str(e)}")
异步实现代码
import asyncio import aiohttp import logging from rest_framework.response import Response from adrf.decorators import api_view import json logger = logging.getLogger(__name__) @api_view(['GET']) async def index(request): res = await make_api_request("http://{{host}}/v1/completions") logger.info("res is ----> %s", res) return Response(res, status=200) async def make_api_request(url, method="POST", headers=None, params=None, json_data=None, timeout=None): try: json_data = {'prompt': 'Hi, How are you?', 'max_new_tokens': 700, 'temperature': 0.1, 'top_p': 1, 'max_tokens': 700, 'model': 'meta-llama/Llama-2-7b-chat-hf'} async with aiohttp.ClientSession() as session: async with session.request(method, url, headers=headers, params=params, json=json_data, timeout=timeout, ssl=False) as response: content = await response.read() if 'json' in response.headers.get('Content-Type', ''): content = json.loads(content) return content except asyncio.TimeoutError: raise TimeoutError("Request timed out. The server did not respond within the specified timeout period.") except aiohttp.ClientError as e: raise ConnectionError(f"Request error: {str(e)}") except Exception as e: raise Exception(f"Exception error: {str(e)}")
一、apr_socket_recv: Connection reset by peer (54)错误原因
- 服务器资源耗尽:并发50时,Django服务器或外部API的连接数、进程/线程池被耗尽,触发主动连接重置
- 外部API限流:被调用的LLaMA API存在并发连接限制,超过阈值后主动断开连接
- 超时配置不匹配:尽管ab设置了超长超时,但Django或外部API的内部超时更短,导致连接被提前重置
- 系统资源限制:操作系统文件描述符上限过低,无法支撑大量并发连接,触发内核层面的连接重置
二、异步方案性能未达预期的核心原因
- 异步资源复用缺失:每次请求都创建新的
aiohttp.ClientSession,频繁的会话创建销毁抵消了异步IO的性能优势 - 外部API成为瓶颈:无论同步还是异步实现,最终都依赖外部LLaMA API的响应速度,若外部API本身处理缓慢,异步无法突破该瓶颈
- ASGI服务器配置不足:Daphne默认配置未充分利用异步特性,如worker数量、并发连接数设置不合理
- 低并发下同步优势:WSGI开发服务器在20并发下有足够的进程/线程处理请求,外部API的响应时间掩盖了同步阻塞的劣势
三、高负载性能优化方案
通用优化(同步/异步均适用)
- 提升系统资源限制:临时调整Linux文件描述符上限:
ulimit -n 65535,修改/etc/security/limits.conf实现永久生效 - 外部API优化:
- 扩展LLaMA服务实例数,提升其并发处理能力
- 对相同prompt的请求启用缓存,直接返回缓存结果
- Django基础优化:
- 禁用DEBUG模式(生产环境强制要求),减少不必要的日志和调试检查
- 使用Redis等缓存框架,缓存视图响应或外部API返回数据
异步实现专属优化
- 复用aiohttp.ClientSession:全局初始化一个会话实例,避免每次请求重复创建,修改示例:
# 模块级别初始化ClientSession import aiohttp session = aiohttp.ClientSession() async def make_api_request(url, method="POST", headers=None, params=None, json_data=None, timeout=None): try: json_data = {'prompt': 'Hi, How are you?', 'max_new_tokens': 700, 'temperature': 0.1, 'top_p': 1, 'max_tokens': 700, 'model': 'meta-llama/Llama-2-7b-chat-hf'} async with session.request(method, url, headers=headers, params=params, json=json_data, timeout=timeout, ssl=False) as response: content = await response.read() if 'json' in response.headers.get('Content-Type', ''): content = json.loads(content) return content # 异常处理逻辑保持不变
注意:需通过Django信号在应用关闭时调用await session.close()释放资源。
优化Daphne配置:
- 启动时增加worker数量:
daphne -p 8001 --workers 4 myproject.asgi:application - 调整
--http-timeout等参数,匹配外部API的响应时间
- 启动时增加worker数量:
全链路异步化:确保异步视图中无同步阻塞操作,避免混合调用同步ORM、同步第三方库等代码
四、负载测试策略调整
- 分步梯度测试:从10、20并发逐步提升至50、100,观察每个并发级别下的错误率和吞吐量,定位性能阈值
- 增加监控维度:测试时监控服务器CPU、内存、网络连接数、文件描述符使用情况,以及外部API的响应时间和错误率
- 替换测试工具:ab对异步支持有限,改用
wrk或locust进行更精准的异步负载测试,示例wrk命令:
wrk -t4 -c50 -d30s -s post.lua http://127.0.0.1:8001/test
(post.lua为包含POST请求体的自定义脚本)
- 合理设置超时:ab的
-s参数应设置为外部API最大响应时间+缓冲时间,避免超时误判
内容的提问来源于stack exchange,提问作者D.Sunil
相关产品推荐
相关产品推荐

