将芹菜任务的结果链接到分布式组中

时间:2017-03-29 23:45:20

标签: python celery chain

this other question一样,我想从芹菜任务返回的列表中创建一个芹菜组。我们的想法是,第一个任务将返回一个列表,第二个任务会将该列表分解为列表中每个项目的并发任务。

计划是在下载内容时使用它。第一个任务从网站获取链接,第二个任务是下载页面,处理页面然后将其上传到s3的链。最后,一旦完成所有子页面,网站就会在我们的数据库中标记为已完成。类似的东西:

chain(
    get_links_from_website.si('https://www.google.com'),
    dmap.s(  # <-- Distributed map
        download_sub_page.s() | 
        process_sub_page.s() | 
        upload_sub_page_to_s3.s()
    ),
    mark_website_done.s()
)

我到目前为止看到的解决方案似乎已经做好了充分的工作,但是当第二个任务是一个链条时失败,因为clone没有进行深度复制的问题(见{{3详情):

@task
def dmap(it, callback):
    # Map a callback over an iterator and return as a group
    callback = subtask(callback)
    return group(callback.clone([arg,]) for arg in it)()

它还有一个问题,如果iterable是10,000个项目,它将创建一个包含10,000个项目的组。正如你想象的那样,这会炸毁我们的内存使用量。

所以,我正在寻找的是dmap的一种方式:

  • 不会通过创建怪异的群体来炸毁RAM(也许还有一种方法可以通过可迭代的群体?)
  • 适用于芹菜链而不会出现深层扫描问题。

2 个答案:

答案 0 :(得分:2)

芹菜画布提供chunks将任务拆分为块。不幸的是,这不适用于像chain,group这样的原语。

您可以使用芹菜信号来防止dmap / clone出现问题。

ch = chain(
    download_sub_page.s(),
    process_sub_page.s(),
    upload_sub_page.s(),
)

@task_success.connect(sender='get_links_from_website')
def task_success_handler(sender=None, headers=None, body=None, **kwargs):
    result = kwargs['result']    
    header = [ch(i) for i in result]
    callback = mark_website_done.si()
    chord(header)(callback)

创建一个用于处理页面的链,并使用和弦将最后一个任务挂钩。只要get_links_from_website成功运行,就会执行此功能。

根据链所用的时间,您还可以在某处保存get_links_from_website的结果。然后遍历一批它们以排队链,并且使用最后一批,您可以将回调挂钩到最后一个任务。

答案 1 :(得分:1)

这有点hacky但是我们使用deepcopy来克隆回调,这修复了Signature的浅拷贝的错误

'\"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe\"' is not recognized as an internal or external command,
operable program or batch file.

请注意,这仅适用于一个嵌套级别(即回调是链/组/和弦),但不适用于深层嵌套的回调

对于深度嵌套的回调图,我们使用这个hack,它有点慢但是完美无缺

def dmap(it, callback, final=None):
    # Map a callback over an iterator and return as a group
    callback = subtask(callback)

    run_in_parallel = group(subtask(copy.deepcopy(dict(callback))).clone([arg, ]) for arg in it)

    if len(run_in_parallel.tasks) == 0:
        return []

    if final:
        return chord(run_in_parallel)(final)

    return run_in_parallel.delay()

对于组的大小,您始终可以将迭代器拆分为块