Python 芹菜使链条停止运转

Python 芹菜使链条停止运转,python,celery,django-celery,celery-task,celerybeat,Python,Celery,Django Celery,Celery Task,Celerybeat,我有一个定期执行的检查订单任务。它创建了一组任务,这样我就可以计算执行任务所花的时间,并在任务全部完成时执行某些操作(这是res.join[1]和grouped_subs的目的)分组的任务是成对的链式任务 我想要的是当第一个任务不满足条件(失败)时,不要执行链中的第二个任务。我一辈子都搞不懂这一点,我觉得这是作业队列管理器的基本功能。当我尝试在[2]之后注释掉的东西时(引发异常、删除回调)。。。由于某种原因,我们在check_orders中被join()卡住了(它破坏了组)。对于所有这些任务,我

我有一个定期执行的检查订单任务。它创建了一组任务,这样我就可以计算执行任务所花的时间,并在任务全部完成时执行某些操作(这是res.join[1]和grouped_subs的目的)分组的任务是成对的链式任务

我想要的是当第一个任务不满足条件(失败)时,不要执行链中的第二个任务。我一辈子都搞不懂这一点,我觉得这是作业队列管理器的基本功能。当我尝试在[2]之后注释掉的东西时(引发异常、删除回调)。。。由于某种原因,我们在check_orders中被join()卡住了(它破坏了组)。对于所有这些任务,我也尝试将ignore_result设置为False,但仍然不起作用

@task(ignore_result=True)
def check_orders():
    # check all the orders and send out appropriate notifications
    grouped_subs = []

    for thingy in things:
       ...

        grouped_subs.append(chain(is_room_open.subtask((args_sub_1, )), 
                        notify.subtask((args_sub_2, ), immutable=True)))

    res = group(grouped_subs).apply_async()

    res.join()         #[1]
    logger.info('Done checking orders at %s' % current_task.request.id))

@task(ignore_result=True)
def is_room_open(args_sub_1):
    #something time consuming
    if http_req_and_parse(args_sub_1):
        # go on and do the notify task
        return True
    else:
        # [2]
        # STOP THE CHAIN SOMEHOW! Don't execute the rest of the chain, how?
        # None of the following things work:
        # is_room_open.update_state(state='FAILURE')
        # raise celery.exceptions.Ignore()
        # raise Exception('spam', 'eggs')
        # current_task.request.callbacks[:] = []

@task(ignore_result=True)
def notify(args_sub_2):
    # something else time consuming, only do this if the first part of the chain 
    # passed a test (the chained tasks before this were 'successful'
    notify_user(args_sub_2)

首先,如果函数中存在异常
ignore\u result
似乎对您没有帮助

其次,您使用immutable=True,这意味着下一个函数(在我们的例子中是notify)不接受额外的参数。当然,如果适合您的决定,您应该使用
notify.subtask((args\u sub\u 2,),immutable=False)

第三,您可以使用快捷方式:

notify.si(args\u sub\u 2)
而不是
notify.subtask((args\u sub\u 2,),immutable=True)

是开着的房间吗.s(args\u sub\u 1)
相反
是开着的房间吗.subtask((args\u sub\u 1,)

尝试使用it代码:

如果要捕获异常,则必须使用回调

文件室是否打开.s(args\u sub\u 1,link\u error=log\u error.s())


在我看来,这是一个在文档中没有得到足够重视的常见用例

假设您希望中途中止链,同时仍将成功报告为已完成任务的状态,并且不发送任何错误日志或诸如此类的内容(否则您可以引发异常),则实现此目的的方法是:

@app.task(bind=True)  # Note that we need bind=True for self to work
def task1(self, other_args):
    #do_stuff
    if end_chain:
        self.request.callbacks = None
        return
    #Other stuff to do if end_chain is False
在你的例子中:

@app.task(ignore_result=True, bind=True)
def is_room_open(self, args_sub_1):
    #something time consuming
    if http_req_and_parse(args_sub_1):
        # go on and do the notify task
        return True
    else:
        self.request.callbacks = None
会有用的。请注意,您可以使用@abbasov alexander所述的快捷方式
.si()
,而不是
ignore\u result=True
subtask()


按照@PhilipGarnero在评论中的建议,编辑为使用急切模式工作。

这令人难以置信,因为如此常见的情况在任何官方文件中都没有处理。我不得不处理同样的问题(但是使用
共享_任务
绑定
选项,这样我们就可以看到
self
对象),因此我编写了一个自定义装饰程序,自动处理撤销:

def revoke_chain_authority(a_shared_task):
    """
    @see: https://gist.github.com/bloudermilk/2173940
    @param a_shared_task: a @shared_task(bind=True) celery function.
    @return:
    """
    @wraps(a_shared_task)
    def inner(self, *args, **kwargs):
        try:
            return a_shared_task(self, *args, **kwargs)
        except RevokeChainRequested, e:
            # Drop subsequent tasks in chain (if not EAGER mode)
            if self.request.callbacks:
                self.request.callbacks[:] = []
            return e.return_value

    return inner
您可以按如下方式使用它:

@shared_task(bind=True)
@revoke_chain_authority
def apply_fetching_decision(self, latitude, longitude):
    #...

    if condition:
        raise RevokeChainRequested(False)
请参阅完整的解释。
希望有帮助

谢谢你提供的关于快捷方式的提示。虽然这会奏效,但并不能解决我的问题。如果第一个任务失败,我希望第二个任务永远不要执行。此解决方案仍然存在每次启动第二个任务的开销,与第一个任务的结果无关。我想停止执行锁链,我明白你的意思。如果任务引发异常,则链的执行将停止。它的行为是默认的。您不需要搜索特殊决策。@Alexander,引发异常无法正常工作。“当我尝试在[2]之后注释掉的内容时(引发异常、删除回调)…我们会因为某种原因在检查顺序中陷入join()状态(它会中断组)。”如果您在急切模式下运行任务,上述操作将停止任务。我将
self.request.callbacks[:]=[]
替换为
self.request.callbacks=None
,现在它在两种情况下都能工作。如果它在两种情况下都能工作,那么我们建议这样做。感谢您的评论,以改进答案:)显然,它不再适用于芹菜4.0,但是
self.request.chain=None
可以。self.request.callbacks=None和self.request.chain=None对我都不起作用。我通过返回一个布尔值来解决这个问题,该布尔值指示是否应该中止,并在链的后续任务中接受它作为参数。谢谢。非常好的解决方案。现在看来
callbacks
变量是一个元组,因此在尝试执行该操作时返回一个错误:
self.request.callbacks[:]=[]''换行符''类型错误:'tuple'对象不支持芹菜4.0的项目分配
,您可以使用此答案-这是否回答了您的问题?
def revoke_chain_authority(a_shared_task):
    """
    @see: https://gist.github.com/bloudermilk/2173940
    @param a_shared_task: a @shared_task(bind=True) celery function.
    @return:
    """
    @wraps(a_shared_task)
    def inner(self, *args, **kwargs):
        try:
            return a_shared_task(self, *args, **kwargs)
        except RevokeChainRequested, e:
            # Drop subsequent tasks in chain (if not EAGER mode)
            if self.request.callbacks:
                self.request.callbacks[:] = []
            return e.return_value

    return inner
@shared_task(bind=True)
@revoke_chain_authority
def apply_fetching_decision(self, latitude, longitude):
    #...

    if condition:
        raise RevokeChainRequested(False)