我有一个check_orders任务,定期执行。它创建了一组任务,以便我可以计算执行任务所花费的时间,并在完成所有操作时执行某些操作(这是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)
答案 0 :(得分:14)
在我看来,这是一个常见的用例,在文档中没有得到足够的爱。
假设您想在中途中止链,同时仍然将SUCCESS报告为已完成任务的状态,并且不发送任何错误日志或诸如此类的东西(否则您只能引发异常),那么实现此目的的方法是:< / p>
@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
所述的快捷方式ignore_result=True
,而不是subtask()
和.si()
。
按照@PhilipGarnero在评论中的建议编辑使用EAGER模式。
答案 1 :(得分:7)
令人难以置信的是,任何官方文件都没有对待这种常见案例。我不得不应对同样的问题(但使用shared_tasks
和bind
选项,因此我们可以看到self
对象),所以我编写了一个自动处理撤销的自定义装饰器: / p>
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)
请参阅完整说明here。 希望它有所帮助!
答案 2 :(得分:2)
首先,似乎进入函数存在异常ignore_result
并没有帮助你。
其次,您使用 immutable = True 这意味着下一个函数(在我们的例子中是 notify )不会采用其他参数。如果它适合您的决定,您当然应该使用notify.subtask((args_sub_2, ), immutable=False)
。
第三,您可以使用快捷方式:
notify.si(args_sub_2)
代替notify.subtask((args_sub_2, ), immutable=True)
和
is_room_open.s(args_sub_1)
代替is_room_open.subtask((args_sub_1, ))
尝试使用代码:
@task 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.s(args_sub_1), notify.s(args_sub_2))) res = group(grouped_subs).apply_async() res.join() #[1] logger.info('Done checking orders at %s' % current_task.request.id)) @task 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[:] = [] return False @task def notify(result, args_sub_2): if result: # 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) return True return False
如果你想要捕获异常,你必须使用回调
is_room_open.s(args_sub_1, link_error=log_error.s())
from proj.celery import celery @celery.task def log_error(task_id): result = celery.AsyncResult(task_id) result.get(propagate=False) # make sure result written. with open(os.path.join('/var/errors', task_id), 'a') as fh: fh.write('--\n\n%s %s %s' % ( task_id, result.result, result.traceback))