【问题标题】:How to avoid slack command timeout error?如何避免松弛命令超时错误?
【发布时间】:2016-01-20 10:04:29
【问题描述】:

我正在使用 slack 命令(python 代码在此后面运行),它工作正常,但这给出了错误

This slash command experienced a problem: 'Timeout was reached' (error detail provided only to team owning command).

如何避免这种情况?

【问题讨论】:

    标签: python slack-api slack


    【解决方案1】:

    根据 Slack slash command documentation,您需要在 3000 毫秒(三秒)内响应。如果您的命令需要更长的时间,那么您会收到 Timeout was reached 错误。您的代码显然不会停止运行,但用户不会得到任何对其命令的响应。

    3 秒对于您的命令可以即时访问数据的快速操作来说是可以的,但如果您调用外部 API 或执行一些复杂的操作,则可能不够长。如果您确实需要更长时间,请参阅文档的延迟响应和多重响应部分:

    1. 验证请求是否正常。
    2. 立即返回200 响应,可能类似于{'text': 'ok, got that'}
    3. 去执行您想要执行的实际操作。
    4. 在原始请求中,您获得了一个唯一的response_url 参数。使用您的后续消息向该 URL 发出 POST 请求:
      • Content-type 必须是 application/json
      • 正文为 JSON 编码消息:{'text': 'all done :)'}
      • 您可以返回临时或频道内响应,并添加与直接方法相同的附件

    根据文档,“您可以在用户调用的 30 分钟内响应用户命令最多 5 次”。

    【讨论】:

    • 我只是使用return语句向用户发送响应,如何在python中发送多个响应?
    • @abc 不幸的是,你需要让你的代码更复杂——也许通过类似Celery 的方式。您使用的是什么网络服务器/框架/环境?
    • @rcoup 知道我在这里缺少什么吗? stackoverflow.com/questions/36195924/…
    【解决方案2】:

    在我自己处理了这个问题并将我的 Flask 应用程序托管在 Heroku 上之后,我发现最简单的解决方案是使用线程。我按照这里的例子: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xi-email-support

    from threading import Thread
    
    def backgroundworker(somedata,response_url):
    
        # your task
    
        payload = {"text":"your task is complete",
                    "username": "bot"}
    
        requests.post(response_url,data=json.dumps(payload))    
    
    @app.route('/appmethodaddress',methods=['POST','GET'])
    def receptionist():
    
        response_url = request.form.get("response_url")
    
        somedata = {}
    
        thr = Thread(target=backgroundworker, args=[somedata,response_url])
        thr.start()
    
        return jsonify(message= "working on your request")  
    

    所有缓慢繁重的工作都由backgroundworker() 函数执行。我的 slack 命令指向https://myappaddress.com/appmethodaddress,其中receptionist() 函数获取接收到的Slack 消息的response_url,并将其与任何其他可选数据一起传递给backgroundworker()。由于该过程现在已拆分,因此它几乎立即将 "working on your request" 消息返回到您的 Slack 频道,并在完成后 backgroundworker() 发送第二条消息 "your task is complete"

    【讨论】:

    • 我这样做了,后台工作人员工作了,但问题是它没有将响应发送回同一通道,它只是在烧瓶中打印出结果。那么我怎样才能将后台处理的响应发送到同一个 slackchannel 呢?
    • 如果没有thread.join,如何确保线程在所需操作结束后立即终止?
    【解决方案3】:

    我也经常遇到这个错误:

    “该死 - 斜杠命令不起作用(错误消息:Timeout was reached)。在斜杠命令中管理命令”

    我正在编写一个Slack slash-command "bot" on AWS Lambda,有时需要执行慢速操作(调用其他外部 API 等)。在某些情况下,Lambda 函数需要超过 3 秒的时间,从而导致 Slack 出现 Timeout was reached 错误。

    我在这里找到了@rcoup 的出色答案,并将其应用于 AWS Lambda 的上下文中。错误不再出现。

    我使用两个独立的 Lambda 函数完成了这项工作。一种是“调度员”或“接待员”,它用“200 OK”迎接传入的 Slack 斜杠命令,并向用户返回简单的“Ok, got that”类型的消息。另一个是实际的“worker” Lambda 函数,它异步启动 long-ish 操作,然后将该操作的结果发布到 Slack response_url

    这是调度员/接待员 Lambda 函数:

    def lambda_handler(event, context):
        req_body = event['body']
    
        try:
            retval = {}
    
            # the param_map contains the 'response_url' that the worker will need to post back to later
            param_map = _formparams_to_dict(req_body)
            # command_list is a sequence of strings in the slash command such as "slashcommand weather pune"
            command_list = param_map['text'].split('+')
    
            # publish SNS message to delegate the actual work to worker lambda function
            message = {
                "param_map": param_map,
                "command_list": command_list
            }
    
            sns_response = sns_client.publish(
                TopicArn=MY_SNS_TOPIC_ARN,
                Message=json.dumps({'default': json.dumps(message)}),
                MessageStructure='json'
            )
    
            retval['text'] = "Ok, working on your slash command ..."
        except Exception as e:
            retval['text'] = '[ERROR] {}'.format(str(e))
    
        return retval
    
    
    def _formparams_to_dict(req_body):
        """ Converts the incoming form_params from Slack into a dictionary. """
        retval = {}
        for val in req_body.split('&'):
            k, v = val.split('=')
            retval[k] = v
        return retval
    

    从上面可以看出,我没有直接从调度程序调用工作 Lambda 函数(尽管这是可能的)。我选择use AWS SNS to publish a message that the worker receives and processes

    基于this StackOverflow answer,这是更好的方法,因为它是非阻塞(异步)和可扩展的。此外,在 AWS Lambda 的上下文中使用 SNS 来解耦这两个函数更容易,直接调用对于这个用例来说更棘手。

    最后,这是我在工作人员 Lambda 函数中使用 SNS 事件的方式:

    def lambda_handler(event, context):
        message = json.loads(event['Records'][0]['Sns']['Message'])
        param_map = message['param_map']
        response_url = param_map['response_url']
    
        command_list = message['command_list']
        main_command = command_list[0].lower()
    
        # process the command as you need to and finally post results to `response_url`
    

    【讨论】:

    • 在 'dispatcher' 和 'worker' Lambda 函数之间拆分工作,就像这里提到的那样是一个不错的设计选择。但是,在我遇到的一种情况下,“调度程序”使用了 3 秒以上,我无法从中删除更多代码(由于 AWS SSM 中的外部资源而存在延迟)。我对此的解决方案是简单地增加调度程序函数的内存,因为在 Lambda 中,CPU 性能与内存设置有关。
    猜你喜欢
    • 2021-02-05
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 2016-07-11
    • 2020-07-31
    • 1970-01-01
    • 2017-01-01
    • 1970-01-01
    相关资源
    最近更新 更多