在django项目中,作为相当繁重的导入管道的一部分,我不得不调用属于不同代码库/应用程序的三个任务。两个应用程序都指向同一个AMQP代理,实际上从另一个应用程序调用任务效果很好。
作为我们工作流程的一部分,我们希望将这三个任务链接起来,并等待整个链返回然后继续(每个任务取决于前一个任务,但我们只关心最后一个任务的结果)。我们必须为我们在数据库中插入的每个项目调用此链。为了尝试加快这个过程,我基本上填充了一个字典,其中包含AsyncResults
对象,在我们启动任务时调用my_chain.apply_async
,然后然后迭代此结果dict以检索我们需要的数据并进一步处理它。
在伪代码中,这看起来像这样:
def launch_chains():
# assuming i need to preserve the order in which the chains are
# called for later processing
result_dict = OrderedDict()
for item in all_items:
# Need to use signature objects because the tasks are on a
# different app / codebase
task_1 = remote_celery_app.signature(
'path.to.my.task1',
args=[whatever],
queue="my_queue"
)
task_2 = remote_celery_app.signature(
# ...
)
item_chain = celery.chain(
task_1,
task_2,
app=remote_celery_app
)
result_dict[item.pk] = item_chain.apply_async()
return result_dict
def process_items():
remote_results = launch_chains()
for item_id, r in remote_results:
data = r.get()
process_data(...)
在相当少的一批物品上,这似乎工作得很好,但是当用较重的一套(大约1600个物体,更接近我们在生产中需要的物体)进行测试时,过程循环结束在致电get
时挂断。
发生的事情似乎很随机:我通常会得到第一个结果,第二个get
调用将无限期挂起。有时它甚至不会得到第一个结果,有时我会设法获得大约30个响应(超出预期的1600个)(在这种情况下,循环实际上会终止)。
启动所有任务并返回数据(我在填写字典时看到远程应用程序上的日志),它只是在尝试获取出错的结果时。
解决此问题的一种方法是将apply_async
调用移至处理循环,如下所示:
def launch_chains():
# for loop, chain creation, etc...
result_dict[item.pk] = item_chain # removing the apply_async call...
return result_dict
def process_items():
remote_results = launch_chains()
for item_id, r in remote_results:
r = r.apply_async() # ... and adding it back here
data = r.get()
process_data(...)
这完全有效,我可以处理所有项目,但它显然需要更长的时间,因为我必须等待每次调用完成,而之前的版本应该允许我开始迭代在仍然填充结果字典时返回的结果。
当在python shell中玩游戏时,通常发生的是我试图处理的第一个链(无论它是第一个被推出的另一个被随机挑选的链)都在{{ 1}}状态,然后所有其他将永久保留SUCCESS
。
我已经能够通过在远程应用程序上创建虚拟任务并使用以下管理命令调用它来重现该行为:
PENDING
我承认我不太了解芹菜和AMQP,所以我不知道这是否是一个移民问题,如果我这样做的话错&#34 ;.但是,由于它在本地工作正常,我很确定问题来自两个应用程序之间的通信。
我在django应用程序上使用芹菜3.1.16,远程应用程序是4.0.2 ...由于一大堆原因,我无法在django端升级,不确定是否我将能够在远程版本上降级(快速尝试中断,因为代码使用了4.0中的各种新内容)。
有什么想法吗?如果需要,请不要犹豫,要求更多的配置信息,现在不能再考虑更多。