Python 芹菜能否将状态更新传递给非阻塞调用方?

Python 芹菜能否将状态更新传递给非阻塞调用方?,python,asynchronous,flask,celery,Python,Asynchronous,Flask,Celery,我正在使用异步执行一组操作。有很多这样的操作,每个操作都可能需要很长的时间,因此我不想将结果发送回芹菜工作者函数的返回值,而是希望将它们作为自定义状态更新一次发送回一个。这样,调用者就可以实现带有更改状态回调的进度条,并且worker函数的返回值可以是常量大小,而不是操作数的线性 下面是一个简单的示例,我使用芹菜工作者函数add_pairs\u of_numbers来添加一组成对的数字,为每个添加的数字发送一个自定义状态更新 #!/usr/bin/env python """ Run work

我正在使用异步执行一组操作。有很多这样的操作,每个操作都可能需要很长的时间,因此我不想将结果发送回芹菜工作者函数的返回值,而是希望将它们作为自定义状态更新一次发送回一个。这样,调用者就可以实现带有更改状态回调的进度条,并且worker函数的返回值可以是常量大小,而不是操作数的线性

下面是一个简单的示例,我使用芹菜工作者函数
add_pairs\u of_numbers
来添加一组成对的数字,为每个添加的数字发送一个自定义状态更新

#!/usr/bin/env python

"""
Run worker with:

    celery -A tasks worker --loglevel=info
"""
from celery import Celery

app = Celery("tasks", broker="pyamqp://guest@localhost//", backend="rpc://")

@app.task(bind=True)
def add_pairs_of_numbers(self, pairs):
    for x, y in pairs:
        self.update_state(state="SUM", meta={"x":x, "y":y, "x+y":x+y})
    return len(pairs)

def handle_message(message):
    if message["status"] == "SUM":
        x = message["result"]["x"]
        y = message["result"]["y"]
        print(f"Message: {x} + {y} = {x+y}")

def non_looping(*pairs):
    task = add_pairs_of_numbers.delay(pairs)
    result = task.get(on_message=handle_message)
    print(result)

def looping(*pairs):
    task = add_pairs_of_numbers.delay(pairs)
    print(task)
    while True:
        pass

if __name__ == "__main__":
    import sys

    if sys.argv[1:] and sys.argv[1] == "looping":
        looping((3,4), (2,7), (5,5))
    else:
        non_looping((3,4), (2,7), (5,5))
如果只运行
/tasks
,它将执行
非循环
功能。这就是标准的芹菜:对worker函数进行延迟调用,然后使用
get
等待结果。
handle\u message
回调函数打印每条消息,并返回添加的对数作为结果。这就是我想要的

$ ./task.py
Message: 3 + 4 = 7
Message: 2 + 7 = 9
Message: 5 + 5 = 10
3
虽然对于这个简单的示例,非循环场景已经足够了,但我尝试完成的现实任务是处理一批文件,而不是添加成对的数字。此外,客户端是RESTAPI,因此不能包含任何阻塞
get
调用。在上面的脚本中,我使用
循环
函数模拟此约束。此函数启动异步芹菜任务,但不等待响应。(随后的无限
while
循环模拟web服务器继续运行和处理其他请求。)

如果使用参数“looping”运行脚本,它将运行此代码路径。在这里,它立即打印芹菜任务ID,然后放入无限循环

$ ./tasks.py looping
a39c54d3-2946-4f4e-a465-4cc3adc6cbe5
芹菜工人日志显示执行了添加操作,但是调用者没有定义回调函数,因此它永远不会得到结果

(我意识到这个特定的例子是令人尴尬的并行,所以我可以使用它将其划分为多个任务。但是,在我的非简化现实世界中,我有一些无法并行的任务。)

我想要的是能够在
循环
场景中指定回调。像这样的

def looping(*pairs):
    task = add_pairs_of_numbers.delay(pairs, callback=handle_message) # There is no such callback.
    print(task)
    while True:
        pass
在芹菜文档和我能在网上找到的所有示例(例如)中,没有办法将回调函数定义为
delay
调用或其
apply\u async
等效调用的一部分。只能将一个指定为
get
回调的一部分。这让我觉得这是一个有意的设计决定

在我的RESTAPI场景中,我可以通过让芹菜工人进程以HTTP post的形式将“状态更新”发送回Flask服务器来解决这个问题,但这似乎很奇怪,因为我开始在芹菜中已经存在的HTTP中复制消息传递逻辑


是否有任何方法可以编写我的
循环
场景,以便调用者在不进行阻塞调用的情况下接收回调,或者这在芹菜中是明确禁止的?

芹菜不支持这种模式,尽管您可以(某种程度上)通过向任务发布自定义状态更新来欺骗它

使用update_state()更新任务的状态:

def upload_files(self, filenames):
    for i, file in enumerate(filenames):
        if not self.request.called_directly:
            self.update_state(state='PROGRESS',
                meta={'current': i, 'total': len(filenames)})```
芹菜不支持这种模式的原因是,任务生产者(调用者)与任务消费者(工作者)之间强烈解耦,两者之间的唯一通信是代理以支持生产者与消费者之间的通信,结果后端支持消费者与生产者之间的通信。当前最接近的方法是轮询任务状态或编写自定义结果后端,该后端将允许您通过AMP RPC或redis订阅发布事件