Как мы можем создать асинхронный API в django? - PullRequest
0 голосов
/ 25 мая 2020

Я хочу создать сторонний API-интерфейс чат-бота, который будет асинхронным и будет отвечать «ОК» после 10-секундной паузы. в виду:

import time
from celery import shared_task

@shared_task
def wait():
    time.sleep(10)
    return "ok"

# views.py
def api(request):
    a = wait.delay()
    work = AsyncResult(a.id)
    while True:
        if work.ready():
           return work.get(timeout=1)

Но это решение работает синхронно и без разницы. Как мы можем сделать его асинхронным, не прося пользователя продолжать запрашивать до получения результата?

Ответы [ 3 ]

3 голосов
/ 30 мая 2020

Как упоминалось в ответе @ Blusky: асинхронный API будет существовать в django 3.X. не раньше .

Если это не вариант, то ответ будет просто no .

Обратите внимание, что даже с django 3.X любой код django, который обращается к базе данных, будет не асинхронным, он должен быть выполнен в потоке (пуле потоков)

Celery предназначен для фоновых задач или отложенные задачи, но сельдерей никогда не вернет HTTP-ответ, поскольку он не получил HTTP-запрос, на который должен ответить. Celery также не поддерживает asyncio.

Возможно, вам придется подумать об изменении вашей архитектуры / реализации. Посмотрите на свою общую проблему и спросите себя, действительно ли вам нужен асинхронный API с Django.

Этот API предназначен для приложений браузера или для приложений между компьютерами?

Может ли ваш клиент использовать веб-сокетов и ждать ответа?

Не могли бы вы разделить блокирующую и неблокирующую части на стороне вашего сервера? Используйте django для всего, что не блокируется, для всего периодаi c / deferred (django + celelry) и реализуйте асинхронную часть с помощью плагинов веб-сервера или python кода ASGI или веб-сокетов.

Некоторые идеи

Используйте Django + nginx nchan (если ваш веб-сервер - сельдерей)

Ссылка на nchan: https://nchan.io/ ваш Вызов API создаст идентификатор задачи, запустит задачу с сельдереем, немедленно вернет идентификатор задачи или URL-адрес опроса.

URL-адрес опроса будет обрабатываться, например, через длинный канал опроса nchan. ваш клиент подключается к URL-адресу, соответствующему длинному каналу опроса nchan, и сельдерей деблокирует его всякий раз, когда ваша задача завершена (десятки закончились)

Используйте Django + сервер ASGI + один кодированный вручную просмотр и использование стратегии, аналогичной nginx nchan

Тот же logi c, как указано выше, но вы не используете nginx nchan, а реализуете его самостоятельно

Используйте сервер ASGI + неблокирующую структуру (или просто некоторые вручную закодированные представления ASGI) для всех блокирующих URL-адресов и Django для остальных.

Они могут обмениваться данными через базу данных, локально файлы или через локальные HTTP-запросы.

Просто продолжайте блокировать и запускайте на свой сервер достаточное количество рабочих процессов / потоков

Это, вероятно, худшее предложение, но если это просто для личного использования, и вы знаете, сколько запросов у вас будет параллельно, тогда просто убедитесь, что у вас достаточно Django воркеров, чтобы вы могли позволить себе блокировку. В этом случае вы бы заблокировали весь Django воркер для каждого низкого запроса.

Используйте веб-сокеты. например, с модулем каналов для Django

Веб-сокеты могут быть реализованы с более ранними версиями django (> = 2.2) с помощью модуля django каналов (pip install channels) ( https://github.com/django/channels)

Вам нужен ASGI-сервер для сервера асинхронной части. Вы можете использовать, например, Daphne ot uvicorn (канал do c это довольно хорошо объясняет)

Приложение 2020-06-01: простой asyn c пример синхронного вызова django код

В следующем коде используется модуль starlette, поскольку он кажется довольно простым и маленьким

miniasyncio.py

import asyncio
import concurrent.futures
import os
import django
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pjt.settings')
django.setup()

from django_app.xxx import synchronous_func1
from django_app.xxx import synchronous_func2

executor = concurrent.futures.ThreadPoolExecutor(max_workers=2)

async def simple_slow(request):
    """ simple function, that sleeps in an async matter """
    await asyncio.sleep(5)
    return Response('hello world')

async def call_slow_dj_funcs(request):
    """ slow django code will be called in a thread pool """
    loop = asyncio.get_running_loop()
    future_func1 = executor.submit(synchronous_func1)
    func1_result = future_func1.result()
    future_func2 = executor.submit(synchronous_func2)
    func2_result = future_func2.result()
    response_txt = "OK"
    return Response(response_txt, media_type="text/plain")

routes = [
    Route("/simple", endpoint=simple_slow),
    Route("/slow_dj_funcs", endpoint=call_slow_dj_funcs),
]

app = Starlette(debug=True, routes=routes)

вы можете, например, запустить это код с

pip install uvicorn
uvicorn --port 8002 miniasyncio:app

, затем на вашем веб-сервере перенаправьте эти c URL-адреса на uvicorn, а не на ваш django сервер приложений.

2 голосов
/ 31 мая 2020

Checkout Django 3 Поддержка ASGI (интерфейс асинхронного серверного шлюза):
https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/

0 голосов
/ 29 мая 2020

Лучшим вариантом является использование Futur async API, который будет предложен Django в версии 3.1 (которая уже доступна в альфа-версии)

https://docs.djangoproject.com/en/dev/releases/3.1/#asynchronous -views- and-middleware-support

(однако вам потребуется использовать ASGI Web Worker, чтобы он работал правильно)

...