Django. Асинхронные запросы выполняются только в одном цикле событий async

Я пытаюсь выяснить, как использовать поддержку асинхронных запросов в Django 4.0. Есть одна очень странная вещь, которую я не могу понять: Django (uvicorn или asgi.py) создает только один цикл событий для двух разных представлений, несмотря на то, что я указал max_workers=8 в настройках uvicorn.

Это может привести к ситуации, когда у нас много представлений (например, 100) и в этих представлениях много логики, связанной с процессором (например, условия, циклы, вычисления и т.д.) и много логики, связанной с IO. Используя async/await, мы ускоряем и оптимизируем все операции, связанные с IO, но логика, связанная с процессором, замедляет работу нашего цикла событий.

Вместо использования event-loop для каждого процесса (рабочего), он использует только один цикл, в то время как другие рабочие (7) стоят на месте.

Я думаю, это ненормальное поведение. А ты что думаешь?

Посмотрите код ниже, чтобы увидеть больше подробностей:

views.py

import asyncio
import os
import random
import time

from django.http import HttpResponse
from django.utils.decorators import classonlymethod
from django.views import View


class AsyncBaseView(View):
    @classonlymethod
    def as_view(cls, **initkwargs):
        view = super().as_view(**initkwargs)
        view._is_coroutine = asyncio.coroutines._is_coroutine
        return view


class AsyncTestView(AsyncBaseView):
    async def get(self, request, *args, **kwargs):
        r = random.randint(1,4)
        print('Recieved', r)
        print('PID = ', os.getpid())

        # Simulate long code-inside operations (conditions, loops, calculations, ets)
        time.sleep(r)

        # Simulate external API call
        await asyncio.sleep(r)

        return HttpResponse('OK')


class AsyncTestView2(AsyncBaseView):
    async def get(self, request, *args, **kwargs):
        r = random.randint(1,4)
        print('Recieved', r)
        print('PID = ', os.getpid())

        # Simulate long code-inside operations (conditions, loops, calculations, ets)
        time.sleep(r)

        # Simulate external API call
        await asyncio.sleep(r)

        return HttpResponse('OK')

urls.py

from django.urls import path, re_path
from apps.api_v1 import async_views

urlpatterns = [
    # test async
    path('async/', async_views.AsyncTestView.as_view(), name='async'),
    path('async2/', async_views.AsyncTestView2.as_view(), name='async'),
]

выполнение команды сервера:

uvicorn main.asgi:application --host 0.0.0.0 --port 8000 --workers=8 --reload

вывод (клиент отправляет GET-запрос в асинхронном режиме):

nginx | 172.31.0.1 - - [07/Dec/2022:13:13:07 +0000] "GET /api/v1/async2 HTTP/1.1" 301 0 "-" "Python/3.9 aiohttp/3.8.3" "-"
nginx | 172.31.0.1 - - [07/Dec/2022:13:13:07 +0000] "GET /api/v1/async2 HTTP/1.1" 301 0 "-" "Python/3.9 aiohttp/3.8.3" "-"
nginx | 172.31.0.1 - - [07/Dec/2022:13:13:07 +0000] "GET /api/v1/async HTTP/1.1" 301 0 "-" "Python/3.9 aiohttp/3.8.3" "-"
nginx | 172.31.0.1 - - [07/Dec/2022:13:13:07 +0000] "GET /api/v1/async HTTP/1.1" 301 0 "-" "Python/3.9 aiohttp/3.8.3" "-"
web                         | Recieved 1
web                         | PID =  176
web                         | Recieved 3
web                         | PID =  176
web                         | Recieved 2
web                         | PID =  176
web                         | Recieved 1
web                         | PID =  176

Посмотрите на номер PID - все запросы обрабатываются только одним рабочим!

Вернуться на верх