我有一个芹菜队列,只有一个工人(--concurrency=1
)。我有两个任务。一个调用API(call_api_task
)的人。另一个(other_task
)调用一个函数,该函数又调用API任务并等待结果。
def update_data():
data_to_api = '{"hello": "world"}'
api_response = call_api_task.subtask(data_to_api).delay().get()
return api_response
@celery.task
def call_api_task(data_to_api):
# Call api with data_to_api
return '{"success": "true"}'
@celery.task
def other_task():
response = update_data()
# Do something with response
我遇到的问题是,当other_task
被调用时,它永远不会完成。基于Celery docs我认为将任务称为子任务可以避免这个问题,但似乎我错过了一些东西。
向update_data
添加一个参数,以便让它知道它是从other_task
调用的。然后拨打call_api_task
,不要subtask
& delay
。
other_task
和update_data
之间还有其他几个函数,这意味着我必须经常传递该参数。这会导致代码混乱。在update_data
中添加代码,通过检查billiard.current_process()
属性来检测是否通过Celery任务调用了该代码。
将other_task
放在不同的Celery队列中。
update_data
如何在不使用上述可能的解决方案#1或#2的情况下从call_api_task
调用时直接调用other_task
(而不是任务)?
这个问题&答案是专门解决“调用另一个远程任务的celery任务(它位于另一个芹菜应用程序,另一个服务器......)”。对于我的场景,情况并非如此。如上所述,我可以将other_task
放在一个单独的Celery队列中,这个队列会产生与此问题的场景相同的最终结果,但我认为应该有更优雅的方法来实现这一目标。
答案 0 :(得分:0)
您似乎正在寻找一款干净利落的Celery API。
FWIW,您可以将Task
子类化为一个,这样您就可以获得您正在寻找的属性,而无需在示例中更改代码。我删除了subtask
。
注意 - 这使用billiard
方法,但将其隔离到代码库的一部分。如果billiard
不是一个选项,您可以使用inspect
...但是那个丑陋的IMO。您可以使用billiard.current_process().daemon
代替下面说明的更粗略的字符串匹配。
否则,这里还有一些有趣的环境变量方法:How can I detect whether I'm running in a Celery worker?
# Python 3
def update_data():
""" OP's method without subtask """
data_to_api = '{"hello": "world"}'
api_response = call_api_task.delay(data_to_api).get()
return api_response
class DummyAsyncResult():
"""
Provides a get() method that returns the value it's initialized with.
"""
def __init__(self, value):
self.value = value
def get(self):
return self.value
class DelayOnceTask(Task):
"""
Running .delay() returns a DummyAsyncResult instance if called
from within a celery worker. Otherwise behaves like a `Task`
"""
def delay(self, *args, **kwargs):
if 'PoolWorker' in billiard.current_process()._name:
return DummyAsyncResult(self(*args, **kwargs))
else:
return super().delay(*args, **kwargs)