提升Django应用性能:比较阻塞与非阻塞实现
我在用Apache Benchmark (ab) 对我的Django应用进行负载测试时,遇到了一个意外的错误(apr_socket_recv: Connection reset by peer (54))。当我把并发请求增加到50时,这个错误就出现了。奇怪的是,即使是20个并发请求时,非阻塞的方式也没有明显的性能提升(吞吐量和阻塞方式一样)。
这个应用有两种实现方式:一种是使用默认的WSGI开发服务器的阻塞方式,另一种是使用ASGI和Daphne服务器的非阻塞异步方式。阻塞的实现依赖于requests库进行同步请求,而非阻塞的方式则在异步视图函数中使用aiohttp。两种实现都是为了向一个外部API发送POST请求并返回响应。
尽管我期待异步方式能有更好的性能,但错误依然存在,性能也没有提升。
我想了解这个错误的根本原因,以及在更高负载下改善性能的可能解决方案。此外,关于如何优化异步实现或更好的负载测试策略的建议也非常欢迎。
我使用ab作为基准测试工具。用于基准测试的命令是: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):
# logger.info(f"is async: {iscoroutinefunction(index)}")
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)}")
1 个回答
1
- 你不应该用Django的
runserver
开发服务器来做性能测试,因为它并不是为了承受重负载而设计的。如果你想测试一个WSGI应用的性能,可以用gunicorn
或uwsgi
来运行这个应用。 - 如果你想比较同步服务器和异步服务器的性能,记得给同步服务器配置足够的工作进程;否则,这样比较就不公平了。
- 因为远程调用需要2秒钟,所以即使你把周围的代码改得更快或者用
async
,对每个请求的处理速度也不会有太大帮助。- 使用
async
代码和异步应用服务器意味着一个异步工作进程可以同时处理多个请求(当这些请求在等待,比如说上面提到的远程调用时)。
- 使用