在队列中重试任务而不会导致监视错误

时间:2016-03-09 08:23:57

标签: python google-app-engine task-queue

我们正在使用Google App Engine和内置的TaskQueues使用Mandrill发送每日更新电子邮件。它们是在特定时间发送的,通常有很多。我们为每封发送的电子邮件安排了一项任务,其中一些失败是因为Mandrill API超时或我们达到某个速率限制。

我们的代码如下:

@app.route('/worker/send_transactional_email/', methods=['POST'])
def worker_send_transactional_mail():

    payload = json.loads(request.values.get('payload'))

    message = {
        'to': [payload.get('to')],
        'subject': payload.get('subject'),
        'from_name': 'Our App',
        'from_email': "noreply@ourapp.local",
        'text': payload.get('body_text')
    }

    mandrill_payload = _get_mandrill_payload(message)

    url = "https://mandrillapp.com/api/1.0/messages/send-template.json"
    req = urllib2.Request(url, mandrill_payload, {'Content-Type': 'application/json'})
    try:
        urllib2.urlopen(req)
    except urllib2.URLError as error:
        logging.info("to: " + unicode(payload.get('to')))
        logging.info("subject: " + unicode(payload.get('subject')))
        logging.info(error.code)
        logging.info(error.read())
        abort(500)
    return 'ok'

解决方案非常有效,因为失败的任务将在稍后重试,然后通常完成。我们遇到的唯一问题是失败的任务会显示在错误日志中,并导致Google Cloud Monitoring报告错误。

我想要的是将上述abort(500)块中的except替换为告诉TaskQueue重试任务但不记录任何错误或其他内容的内容。我知道我可以返回200-299以外的任何状态代码,而TaskQueue会重试但是我不确定正确的方法是返回301或者其他东西因为它具有很大的误导性。

2 个答案:

答案 0 :(得分:1)

如果在队列上配置重试,则代码中不需要更改任何内容,因为500响应将让队列逻辑知道触发重试。除非您达到最大重试次数,否则不需要手动重新入队,在这种情况下,您应该有一些其他方法可以确保您可以明确地跟踪所需的任务完成情况并重新入队或通过检查来捕获最后一次重试X-Appengine-Taskretrycount标题。如文档中所述,make sure tasks are idempotent

答案 1 :(得分:0)

基于@Nick的检查X-Appengine-Taskretrycount请求标头值并设置要终止的处理程序的答案,我在Flask应用程序__init__.py文件中添加了此命令:

from flask import Flask, request, make_response, jsonify
...

@app.before_request
def max_retries_exit():
    max_retries = 5
    task_retries = request.headers.get('X-Appengine-Taskretrycount', '', type=int)
    if task_retries and task_retries > max_retries:
        output = []
        status_code = 200 if request.method in ['GET', 'HEAD'] else 201
        r = make_response(jsonify(output), status_code)
        error_msg = f"Task retries exceeded {task_retries} > {max_retries}"
        r.headers['X-App-Error'] = error_msg
        app.logger.error(error_msg)
        return r
    else:
        return None