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 - все запросы обрабатываются только одним рабочим!