event_loop 在 Django>=3.1 异步视图中存在多长时间
Posted
技术标签:
【中文标题】event_loop 在 Django>=3.1 异步视图中存在多长时间【英文标题】:How long does the event_loop live in a Django>=3.1 async view 【发布时间】:2020-12-10 13:33:28 【问题描述】:我正在使用 Django 3.1 中的新异步视图。
我希望获得的一些好处是在视图已经提供其HttpResponse
之后执行一些简单的即发即弃的“任务”,例如发送推送通知或发送电子邮件。
我不是在寻找像 celery 这样的第三方包的解决方案!
为了测试这个异步视图,我使用了本教程中的一些代码: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")
我用 uvicorn 启动了 django 服务器。
当我向这个视图发出请求时,它会立即返回 HTTP 响应“非阻塞 HTTP 请求”。
与此同时,在 HTTP 响应之后,事件循环继续愉快地打印最多 200 的数字,然后打印“完成”。
这正是我想要用于我的即发即弃任务的行为。
很遗憾,我找不到有关运行此代码的事件循环的生命周期的任何信息。
事件循环的生命周期是多久? 有超时吗? 它取决于什么?在uvicorn上?这是可配置的吗?
有没有讨论这个话题的资源?
【问题讨论】:
【参考方案1】:Django(不提供 ASGI 服务器)不限制事件循环的生命周期。
在这个问题的情况下:
ASGI 服务器:Uvicorn 事件循环:内置 asyncio 事件循环或uvloop
(均不限制其生命周期)
对于 Uvicorn:
事件循环的生命周期有多长? 事件循环的生命周期与工作线程的生命周期一样长。
有超时吗? 不,任务一直运行到完成(除非工作人员没有响应并被 Gunicorn 杀死)。
它取决于什么?在 Uvicorn 上?
Worker 的生命周期主要受限于--limit-max-requests
(Gunicorn:--max-requests
)。
通过指定--limit-max-requests 2
并运行两次视图来亲自查看问题:
uvicorn mysite.asgi:application --limit-max-requests 2
优雅关机
不管生命周期如何限制,我们可能关心的是如何处理关机。
Uvicorn 如何优雅关机?
让我们看看 Uvicorn worker (uvicorn.server.Server
) 是如何优雅关闭请求的。
uvicorn.protocols.http.h11_impl.H11Protocol#__init__:存储对服务器任务的引用
# Shared server state self.server_state = server_state self.tasks = server_state.tasks
uvicorn.protocols.http.h11_impl.H11Protocol#handle_events:将请求任务添加到任务中
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:等待现有任务完成
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)
如何让 Uvicorn worker 等待你的任务?
让我们通过将您的任务添加到服务器任务来搭载它。
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")
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
现在,Uvicorn 工作人员将在正常关机中等待您的任务。
【讨论】:
以上是关于event_loop 在 Django>=3.1 异步视图中存在多长时间的主要内容,如果未能解决你的问题,请参考以下文章
所有任务完成后如何终止python asyncio event_loop