Dan*_*tar 5 python django python-asyncio
我正在使用 Django 3.1 的新异步视图。
我希望拥有的一些好处是在视图已经给出它之后做一些简单的即发即弃的“任务” HttpResponse,比如发送推送通知或发送电子邮件。我不是在寻找像芹菜这样的第三方软件包的解决方案!
为了测试这个异步视图,我使用了本教程中的一些代码:https : //testdriven.io/blog/django-async-views/
async def http_call_async():
for num in range(1, 200):
await asyncio.sleep(1)
print(num)
# TODO: send email async
print('done')
async def async_view(request):
loop = asyncio.get_event_loop()
loop.create_task(http_call_async())
return HttpResponse("Non-blocking HTTP request")
Run Code Online (Sandbox Code Playgroud)
我用 uvicorn 启动了 django 服务器。
当我向该视图发出请求时,它将立即返回 HTTP 响应“非阻塞 HTTP 请求”。
与此同时,在 HTTP 响应之后,事件循环继续愉快地打印最多 200 的数字,然后打印“完成”。
这正是我想用于我的即发即忘任务的行为。
不幸的是,我找不到有关运行此代码的事件循环生命周期的任何信息。
事件循环存在多长时间?有超时吗?它取决于什么?在独角兽上?那可以配置吗?
有没有讨论这个话题的资源?
Django(不提供ASGI服务器)不限制事件循环的生命周期。
对于这个问题:
uvloop(两者都不限制其生命周期)对于独角兽:
事件循环的生命周期有多长?
只要工作线程存在,事件循环就会存在。
有超时吗?
不,任务会一直运行直到完成(除非工作人员没有响应并被 Gunicorn 杀死)。
它取决于什么?在Uvicorn上?
Worker 的生命周期主要受--limit-max-requests(Gunicorn: --max-requests) 限制。
--limit-max-requests 2通过指定并运行视图两次来亲自查看问题:
uvicorn mysite.asgi:application --limit-max-requests 2
Run Code Online (Sandbox Code Playgroud)
不管生命周期如何限制,我们可能关心的是如何处理关闭。
我们来看看Uvicornworker( uvicorn.server.Server)是如何对请求进行优雅关闭的。
uvicorn.protocols.http.h11_impl.H11Protocol#__init__:存储对服务器任务的引用
Run Code Online (Sandbox Code Playgroud)# Shared server state self.server_state = server_state self.tasks = server_state.tasksuvicorn.protocols.http.h11_impl.H11Protocol#handle_events:将请求任务添加到任务中
Run Code Online (Sandbox Code Playgroud)self.cycle = RequestResponseCycle( ... ) task = self.loop.create_task(self.cycle.run_asgi(app)) task.add_done_callback(self.tasks.discard) self.tasks.add(task)uvicorn.server.Server#shutdown:等待现有任务完成
Run Code Online (Sandbox Code Playgroud)if self.server_state.tasks and not self.force_exit: msg = "Waiting for background tasks to complete. (CTRL+C to force quit)" logger.info(msg) while self.server_state.tasks and not self.force_exit: await asyncio.sleep(0.1)
让我们通过将您的任务添加到服务器任务来实现这一点。
async def async_view(request):
loop = asyncio.get_event_loop()
# loop.create_task(http_call_async()) # Replace this
task = loop.create_task(http_call_async()) # with this
server_state = await get_server_state() # Add this
task.add_done_callback(server_state.tasks.discard) # Add this
server_state.tasks.add(task) # Add this
return HttpResponse("Non-blocking HTTP request")
Run Code Online (Sandbox Code Playgroud)
import gc
from uvicorn.server import ServerState
_server_state = None
@sync_to_async
def get_server_state():
global _server_state
if not _server_state:
objects = gc.get_objects()
_server_state = next(o for o in objects if isinstance(o, ServerState))
return _server_state
Run Code Online (Sandbox Code Playgroud)
现在,Uvicorn 工作人员将在正常关闭中等待您的任务。