Повышение производительности приложений Django: Сравнение блокирующей и неблокирующей реализаций
Я столкнулся с непредвиденной ошибкой (apr_socket_recv: Connection reset by peer (54)) при нагрузочном тестировании моего Django-приложения с помощью Apache Benchmark (ab). Эта ошибка возникает, когда я увеличиваю нагрузку до 50 одновременных запросов. Удивительно, но даже при 20 одновременных запросах не наблюдается значительного улучшения производительности при использовании неблокирующего соединения (пропускная способность такая же, как и у блокирующего)
Приложение состоит из двух реализаций: одна использует блокирующий подход с сервером разработки WSGI по умолчанию, а другая - неблокирующий асинхронный подход с ASGI и сервером Daphne. Блокирующая реализация опирается на синхронные запросы с библиотекой requests, в то время как неблокирующий подход использует aiohttp в рамках асинхронной функции представления. Обе реализации направлены на выполнение POST-запросов к внешней конечной точке API и возврат ответов. Несмотря на ожидание лучшей производительности при использовании асинхронного подхода, ошибка сохраняется, а производительность остается на прежнем уровне.
Я хочу понять первопричину этой ошибки и найти возможные решения для повышения производительности при больших нагрузках. Кроме того, любые советы по оптимизации асинхронной реализации или предложения по лучшим стратегиям нагрузочного тестирования будут высоко оценены.
Я использую 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)}")
- Вам не следует проводить бенчмаркинг сервера разработки Django
runserver
; он не предназначен для большой нагрузки. Если вам нужно провести бенчмаркинг WSGI-приложения, используйте, например,gunicorn
илиuwsgi
для его обслуживания. - Если вы хотите сравнить производительность синхронного сервера с асинхронным, убедитесь, что синхронный сервер также имеет достаточное количество рабочих; в противном случае это совсем не равные условия.
- Поскольку удаленный вызов занимает 2 секунды, изменение окружающего кода на более быстрый или
async
не сильно поможет повысить пропускную способность в расчете на один запрос.async
код с асинхронным сервером приложений означает, что один асинхронный рабочий может обрабатывать несколько запросов одновременно (когда запросы ожидают, например, вышеупомянутого удаленного вызова).