Повышение производительности приложений 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 код с асинхронным сервером приложений означает, что один асинхронный рабочий может обрабатывать несколько запросов одновременно (когда запросы ожидают, например, вышеупомянутого удаленного вызова).
Вернуться на верх