我们如何在Django中创建异步API?

问题描述 投票:0回答:3

我想创建一个第三方聊天机器人API,该API是异步的,并在10秒钟的暂停后回复“确定”。

import time

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

# views.py
def api(request):
    return wait()

我尝试了如下所示的芹菜,其中我正在等待芹菜响应以查看自身:

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)

但是此解决方案是同步工作的,没有区别。在不要求用户继续请求直到收到结果之前,我们如何才能使其异步?

python django asynchronous celery django-celery
3个回答
2
投票

检出Django 3 ASGI(异步服务器网关接口)支持:https://docs.djangoproject.com/en/3.0/howto/deployment/asgi/


2
投票

如@Blusky的答案中所述:异步API将存在于Django 3.X中。 不早于

如果这不是一个选择,那么答案就是no

还请注意,即使使用django 3.X,访问数据库的任何django代码也将是not异步的,它必须在线程(线程池)中执行]

Celery适用于后台任务或延迟任务,但是celery不会返回HTTP响应,因为它没有收到应响应的HTTP请求。芹菜也不友好。

您可能不得不考虑更改架构/实现。查看您的总体问题,并问自己是否真的需要使用Django的异步API。

此API是用于浏览器应用程序还是用于机器对机器的应用程序?

您的客户可以使用Web套接字并等待答案吗?

服务器端的

您能否分开阻塞部分和非阻塞部分?将django用于所有不阻塞的事物,所有周期性/延迟的事物(django + celelry),并使用Web服务器插件或python ASGI代码或Web套接字实现异步部分。

一些想法

使用Django + nginx nchan(如果您的Web服务器是celery)

链接到nchan:https://nchan.io/您的API调用将创建一个任务ID,启动一个celery任务,立即返回该任务ID或一个查询URL。

例如,轮询URL将通过nchan long轮询通道进行处理。您的客户端连接到与nchan长轮询通道相对应的url,并且每当您完成任务(十秒结束)时,celery就会对其进行阻止

使用Django + ASGI服务器+一个手工编码的视图,并使用类似的策略来替代nginx nchan

与上述逻辑相同,但是您不使用nginx nchan,而是自己实现

使用ASGI服务器+非阻塞框架(或仅使用一些手工编码的ASGI视图)来处理所有阻塞的URL,其余使用Django。

他们可能通过数据库,本地文件或本地http请求交换数据。

只是保持阻塞并在服务器上抛出足够的工作进程/线程

这可能是最糟糕的建议,但是如果仅用于个人用途,并且您知道并行将有多少个请求,然后只需确保您有足够的Django worker,这样您就可以承受阻塞了。在这种情况下,您将为每个低请求阻止整个Django工作者。

使用websockets。例如与Django的channels模块一起使用]

Websockets可以使用带有django通道模块(pip install channels)(https://github.com/django/channels)的早期版本的django(> = 2.2)实现

您需要一个ASGI服务器来服务异步部分。您可以使用例如Daphne ot uvicorn(频道文档对此进行了很好的解释)

附录2020-06-01:调用同步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

然后在ypur Web服务器上将这些特定的URL路由到uvicorn,而不是django应用程序服务器。


0
投票

[最佳选择是使用futurasyncAPI,它将在Django 3.1版中提出(已经在alpha中提供)

© www.soinside.com 2019 - 2024. All rights reserved.