Django 从芹菜任务接收事件
我有一个长时间运行的芹菜任务,它迭代一系列项目并执行一些操作 任务应该以某种方式报告它当前正在处理的项目,以便最终用户知道任务的进度 目前,我的django应用程序和芹菜一起位于一台服务器上,因此我可以使用django的模型来报告状态,但我计划添加更多远离django的工作人员,因此他们无法访问DB 现在我看到的解决方案很少:Django 从芹菜任务接收事件,django,celery,Django,Celery,我有一个长时间运行的芹菜任务,它迭代一系列项目并执行一些操作 任务应该以某种方式报告它当前正在处理的项目,以便最终用户知道任务的进度 目前,我的django应用程序和芹菜一起位于一台服务器上,因此我可以使用django的模型来报告状态,但我计划添加更多远离django的工作人员,因此他们无法访问DB 现在我看到的解决方案很少: 使用一些存储设备(如redis或mongodb)手动存储中间结果,然后通过网络提供。这让我有点担心,因为例如,如果我要使用redis,那么我应该保持Django端读取状
- 使用一些存储设备(如redis或mongodb)手动存储中间结果,然后通过网络提供。这让我有点担心,因为例如,如果我要使用redis,那么我应该保持Django端读取状态的代码与芹菜任务写入状态的代码同步,因此它们使用相同的键
- 使用REST调用从芹菜向Django报告状态。像
PUThttp://django.com/api/task/123/items_processed
- 可能使用芹菜事件系统,创建类似于
已处理项目
的事件,django会在这些事件上更新计数器
- 创建一个单独的worker,该worker在使用django的服务器上运行,django持有一个只增加
已处理项目数的任务,因此当任务使用一个项目完成时,它会发出
李>增加已处理消息数。延迟(任务id)
e、 g.如果使用redis只是有一个“当前正在处理的任务”键,并将与当前正在处理的项目相关的数据存储在其中。您可以使用类似于从芹菜实例到达用户的东西(您必须能够从客户端到达它,小心不要与您发生冲突)。它可以锁在柜台上,而不是模型本身。看一下
flower
-芹菜分布式任务队列的实时监视器和web管理员:
我希望这能有所帮助。实现你的目标可能有很多方法,但我会这样做 在长时间运行的芹菜任务中,使用以下方法设置进度: 然后,您所要做的就是使用该键发出一个重复的ajaxget请求,并从缓存中检索进度。大致如下:
def task_progress_view(request, *args, **kwargs):
key = request.GET.get('task_key')
progress = cache.get(key)
return HttpResponse(content=json.dumps({'progress': progress}),
content_type="application/json; charset=utf-8")
但这里有一个警告,如果您将服务器作为多个进程运行,请确保使用类似memcached的东西,因为django的本机缓存在进程之间不一致。此外,我可能不会使用芹菜的任务id
作为键,但它足以用于演示目的。如果您不介意客户反复轮询您的后端,看起来不错。这可能没问题,但随着客户数量的增长,成本会越来越高
如果您只需要西芹内部机器生成的任务生命周期事件,则此选项非常适用
或者,您可以使用和WebSocket将更新实时推送到客户端。设置非常简单
频道
添加到您的已安装的应用程序
并设置频道层。例如,使用Redis后端:group\u send
。在这两种情况下,您都不再需要async\u to\u sync
包装器
websocket\u urlpatterns
添加到您的url.py
:没错,我可以通过手动维护一些计数器来利用redis/mongo/mysql。我现在就是这么做的。我想的是如何利用MQ芹菜已经使用的资源。因此,每个任务都会产生一些事件,如
item
,这些事件将在django所在的服务器上处理。因为当项目处理结束时,计数器不是我唯一需要做的事情。可能还有更多任务处理项不知道的东西需要处理。我想你说过数据库(mysql)对工作人员不可用,但现在是这样,但这正是我想要避免的。目前我需要一个简单的计数器,显示用户处理了多少项。但在近期功能中,我需要更多。芹菜已经使用了酒吧/酒吧redis,所以我认为有一种方法可以在芹菜中实现这一点。它有一个事件系统,但事件类型的数量非常有限,而且它们都与芹菜监控有关。否则我需要手动使用redis,这不是很好,因为它只是一个计数器-是的。但是,我需要创建一个发布/订阅频道,这样任何人都可以订阅“项目继续”活动,而不仅仅是一个只增加计数器的功能。因此,我需要创建一个类似于observer/observable的抽象,这样就可以订阅事件和其他事件来触发事件等等。所以我必须创建一个合适的基础设施,它仅限于芹菜提供的活动。对于任务开始/完成的事件,我不需要花。我需要自定义事件,比如任务完成的第X阶段。Flower甚至不记录自定义状态。e、 g“进度”状态更新未反映在Flower中。我想,询问者(和我)想要的是报告类似于
from django.core.cache import cache
@app.task()
def long_running_task(self, *args, **kwargs):
key = "my_task: %s" % self.result.id
...
# do whatever you need to do and set the progress
# using cache:
cache.set(key, progress, timeout="whatever works for you")
...
def task_progress_view(request, *args, **kwargs):
key = request.GET.get('task_key')
progress = cache.get(key)
return HttpResponse(content=json.dumps({'progress': progress}),
content_type="application/json; charset=utf-8")
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [("redis", 6379)]
}
}
}
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebSocketConsumer
class TaskConsumer(WebsocketConsumer):
def connect(self):
self.task_id = self.scope['url_route']['kwargs']['task_id'] # your task's identifier
async_to_sync(self.channel_layer.group_add)(f"tasks-{self.task_id}", self.channel_name)
self.accept()
def disconnect(self, code):
async_to_sync(self.channel_layer.group_discard)(f"tasks-{self.task_id}", self.channel_name)
def item_processed(self, event):
item = event['item']
self.send(text_data=json.dumps(item))
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
...
async_to_sync(get_channel_layer.group_send)(f"tasks-{task.task_id}", {
'type': 'item_processed',
'item': item,
})
websocket_urlpatterns = [
path(r'ws/tasks/<task_id>/', TaskConsumer.as_asgi()),
]
let task_id = 123;
let protocol = location.protocol === 'https:' ? 'wss://' : 'ws://';
let socket = new WebSocket(`${protocol}${window.location.host}/ws/tasks/${task_id}/`);
socket.onmessage = function(event) {
let data = JSON.parse(event.data);
let item = data.item;
// do something with the item (e.g., push it into your state container)
}