芹菜redis后端并不总是返回结果

时间:2016-09-28 21:16:56

标签: python rabbitmq celery

我正在经营芹菜工人:

 -------------- celery@ v3.1.23 (Cipater)
---- **** ----- 
--- * ***  * -- Linux-4.4.0-31-generic-x86_64-with-debian-stretch-sid
-- * - **** --- 
- ** ---------- [config]
- ** ---------- .> app:         __main__:0x7fe76cd42400
- ** ---------- .> transport:   amqp://
- ** ---------- .> results:     redis://
- *** --- * --- .> concurrency: 4 (prefork)
-- ******* ---- 
--- ***** ----- [queues]
 -------------- .> celery           exchange=celery(direct) key=celery
[tasks]
  . tasks.mytask

tasks.py

@celery_app.task(bind=True, ignore_result=False)
def mytask(task):
    r = redis.StrictRedis()
    r.rpush('/task_finished', task.request.id)
    return {'result': 42}

当我尝试运行以下代码时,一个接一个地运行2个任务,它在获取第一个结果时起作用,但是无法返回第二个结果。

import celery.result
import redis

r = redis.StrictRedis()
celery_app = Celery(name="my_long_task", backend="redis://")

while True:
    _, resp = r.blpop('/task_finished')
    task_id = resp.decode('utf-8')
    task = celery.result.AsyncResult(task_id, app=celery_app)
    print(task)
    print(task.result)

将返回:

第一个循环

[1] 990e2d04-5664-4d7c-8a5c-e9cb4ef45e24  
[2] {'result': 42}

第二个循环(无法返回结果):

[3] 8463cc46-0884-4bf7-b838-f0614f74b271  
[4] {}

但是,如果我在celery_app = Celery(name="my_long_task", backend="redis://")循环中实例化while,它每次都会有效 不重新复制celery_app有什么问题?我错过了什么?

修改

等待结果(在延迟的情况下)也不会起作用

while True:
    _, resp = r.blpop('/task_finished')
    task_id = resp.decode('utf-8')
    for i in range(0, 20):
        # Won't work because I need to re instantiate celery_app
        task = celery.result.AsyncResult(task_id, app=celery_app)
        print(task.result)
        time.sleep(1)

1 个答案:

答案 0 :(得分:0)

你有竞争条件。这就是:

  1. 循环到达_, resp = r.blpop('/task_finished')并阻止那里。

  2. 任务执行r.rpush('/task_finished', task.request.id)

  3. 循环解除阻塞,执行task = celery.result.AsyncResult(task_id, app=celery_app)并获得一个空结果,因为该任务尚未将其结果记录到数据库中。

  4. 在芹菜将结果提交到后端后,r.rpush 可能有办法。也许创建一个派生自Task的自定义类就可以了。但那不是我尝试过的。

    但是,您当然可以修改代码以将结果一起存储在任务ID中。类似的东西:

    r.rpush('/task_finished', json.dumps({ "task_id": task.request.id, "result": 42 }))
    

    为了说明,我使用了JSON序列化。你可以使用你想要的任何方案。阅读时:

    _, resp = r.blpop('/task_finished')
    resp = json.loads(resp)
    

    有了这个,您可能希望将ignore_result=False更改为ignore_result=True