Amazon AWS,来自 SQS 队列的消息多次传递

Amazon AWS, messages from SQS queue delivered multiple times

我在 Elastic Beanstalk 上有一个工人 运行,它通过来自队列的消息接受 POST 请求。这些消息会触发耗时数分钟(有时甚至数小时)的长时间操作,并且此操作只执行一次至关重要。

问题是,当我登录worker console查看进程时,消息似乎每分钟一遍又一遍地传递(接收请求触发的方法每分钟调用一次)。我怎样才能摆脱这种行为?

我阅读了文档并将可见超时时间段设置为服务队列和死信队列的最大值(12 小时)。然而,这根本没有帮助。

当我发送消息时,它显示为 "in flight"(我认为这是一种假定的行为,因为队列等待接收删除请求或某种仅在长操作结束)。

有人可以提示我在这种情况下发生了什么吗?我可能错过了配置中的一些重要细节...

编辑:似乎只要消息是 "in flight",每分钟就会重新发送一次消息。一旦我完成了这个过程,消息终于消失了。

看来您处理完后忘记删除了。

消息出队后,有必要将其删除。如果您不显式删除它,SQS 会假定您已将消息出队但无法处理它,因此它会再次出现在队列中。

SQS中有2个超时参数可以设置,都很重要:

  1. WaitTimeSeconds

  2. VisibilityTimeout

1) WaitTimeSeconds = 10 意味着如果队列中有消息,您对 SQS 的调用应该 return 立即,但是如果队列中没有消息, 您的调用将阻塞直到消息到达队列,最多 10 秒。

2) 将消息从队列中取出后,VisibilityTimeout = 60 声明您有 60 秒的时间来处理该消息,否则它将再次出现在队列中。如果您在 60 秒之前处理了该消息,则必须发送 deleteMessage 请求。如果您未能在 60 秒内发送该 deleteMessage 请求,该消息将重新出现在队列中。

如果您在 60 秒后发送 deleteMessage 请求,则该请求将无效,消息仍会重新出现。

你的代码要这样写,如果你的进程失败了,它自然不会发送deleteMessage请求,这样消息自然会再次出现在SQS中。

您可以在此处找到有关 1) 和 2) 的详细信息:

http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/MessageLifecycle.html

http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-long-polling.html

http://boto.readthedocs.org/en/latest/ref/sqs.html#boto.sqs.queue.Queue.get_messages

对于 sqs,您必须手动调用 delete api 以从队列中删除消息。 设置较高的超时值只能确保没有其他轮询器在该时间内收到相同的消息。

你有2个选项。 1. 看到消息后立即删除,然后启动下游流程。 2. 阅读消息,将消息的可见性超时设置为进程的超时值,然后作为进程的一部分,最后一步是删除消息。

这里有一层额外的复杂性,因为您没有直接轮询 SQS 队列; Elastic Beanstalk 部署了一个名为 sqsd 的工作进程,它代表您轮询队列,将它收到的任何消息发布到您的应用程序,并在您响应 200 时从队列中删除它们。

队列上的 VisibilityTimeout 设置控制队列在将消息传递给消费者(在本例中为 sqsd)后等待多长时间,然后它才会假定出现问题并将消息重新传递给其他人。 sqsd 有一个类似的概念(称为 "InactivityTimeout"),它控制 it 在 POST 到您的应用程序之后等待多长时间,然后它假设出现问题并重试。您还需要将其配置得足够高,以便 sqsd 在完成处理之前不会将请求重新发送到您的应用程序。我看到 reports 的另一个 "ProxyTimeout" 设置可能也需要调整。

更一般地说,请记住 exactly-once delivery 在分布式系统中实际上不可能得到保证 - 即使您正确设置了所有超时,因此它在大多数时间都能正常工作,但总有可能你会在完成操作后但在你告诉 SQS 之前崩溃,并且消息将重新传递给其他人。您可以获得的最接近的结果是确保 if 一条消息被传递两次,结果完全相同 - 例如,通过让您的处理逻辑检查它是否即将发送do 已经完成了,如果是,则立即返回 200。