如何在运行另一个任务之前检测做类似工作的Celery任务?

时间:2018-07-17 01:05:25

标签: python celery ipc

我的芹菜任务是对某些数据库存储的实体进行耗时的计算。工作流程是这样的:从数据库获取信息,将其编译为可序列化的对象,然后保存对象。其他任务正在对加载的对象进行其他计算(例如渲染图像)。

但是序列化非常耗时,因此我希望每个实体运行一个任务一段时间,该任务将序列化的对象保存在内存中并处理通过消息传递队列(redis pubsub)传递的客户端请求。如果一段时间没有请求,则任务退出。此后,如果客户端需要完成一些工作,它将运行另一个任务,该任务将加载对象,对其进行处理并为其他任务保持一段时间。此任务应在启动时检查,如果该特定实体上只有一个工作线程,则应避免发生冲突。 那么最好的检查策略是为此实体运行另一个任务吗?

1)第一个想法是将消息发送到与实体关联的某个通道,并等待响应。不好的主意,目标任务可能会忙于计算,等待超时响应只是浪费时间。

2)将celery task-id存储在db中甚至更糟-可以杀死任务,但记录会保留,因此我们需要确保目标任务仍然存在。

3)第三个想法是检查工作程序中正在运行的任务,并检查其状态以获取实体ID(启动时将提供该任务)。似乎还可能会发生一些冲突,即如果计划了多个任务,但尚未运行。

就目前而言,我认为想法1最好是进行如下修改:任务将在启动时以启动时间将消息发送到实体通道,但随后立即开始工作,而不等待响应。然后,它检查消息队列,如果有人响应,他们将比较时间戳和退出较大的任务。似乎足够复杂,有更好的解决方案吗?

1 个答案:

答案 0 :(得分:0)

最终的解决方案是在任务中启动主管线程,该管理员线程会从竞争任务中回复“发现”消息。

工作流程就是这样。

  1. 任务开始,然后使用实体ID订阅Redis PubSub频道
  2. 任务将“发现”消息发送到频道
  3. 任务稍等
  4. 如果发现退出,则在频道中的传入消息中执行任务搜索“答复”。
  5. 任务启动主管线程,该主管线程通过对所有传入的“发现”消息进行“答复”来答复

除了几个任务同时启动(即在工作人员重启之后)外,此方法工作正常。为避免这种需要使订阅过程具有原子性,请使用Redis锁定:

class RedisChannel:
    def __init__(self, channel_id):
        self.channel_id = channel_id
        self.redis = StrictRedis()
        self.channel = self.redis.pubsub()
        with self.redis.lock(channel_id):
            self.channel.subscribe(channel_id)