如何防止重复的SQS消息?

时间:2014-04-24 04:44:39

标签: amazon-web-services amazon-sqs

防止Amazon SQS中重复邮件的最佳方法是什么?我有一个等待被抓取的域的SQS。在我向SQS添加新域之前,我可以检查已保存的数据以查看它是否最近被抓取,以防止重复。

问题在于尚未抓取的域。例如,如果队列中有1000个域尚未被爬网。任何这些链接都可以再次添加,一次又一次。这使我的SQS膨胀成数十万条主要是重复的消息。

如何防止这种情况?有没有办法从队列中删除所有重复项?或者有没有办法在添加消息之前搜索队列中的消息?我觉得任何有SQS的人都必须经历这个问题。

我可以看到的一个选项是,在将域添加到SQS之前,我是否存储了一些数据。但是如果我必须将数据存储两次,那么首先就会破坏使用SQS的重点。

7 个答案:

答案 0 :(得分:32)

正如提到的其他答案,您无法阻止来自SQS的重复邮件。

大多数情况下,您的邮件会在一段时间内传递给您的某个消费者,但 会在某个阶段发生重复。

我不认为这个问题有一个简单的答案,因为它需要提出一个能够应对重复的适当架构,这意味着它本质上是幂等的。

如果分布式架构中的所有工作人员都是幂等的,那么这很容易,因为您不必担心重复。但实际上,这种环境并不存在,某种程度上无法处理它。

我目前正在开展一个项目,要求我解决这个问题,并想出办法来处理它。我认为在这里分享我的想法可能会让其他人受益。这可能是一个对我的想法得到一些反馈的好地方。

事实商店

开发服务是一个不错的主意,这样他们就可以收集理论上可以重播的事实,以便在所有受影响的下游系统中重现相同的状态。

例如,假设您正在为股票交易平台构建消息代理。 (我之前曾经参与过这样的项目,这很糟糕,但也是一次很好的学习经历。

现在让我们说这些交易进来了,有3个系统感兴趣:

  1. 需要保持更新的旧式大型机
  2. 整理所有交易并与FTP服务器上的合作伙伴共享的系统
  3. 记录交易并将股份重新分配给新所有者的服务
  4. 我知道,这有点令人费解,但想法是有一条消息(事实)进入,有各种分布式下游效应。

    现在让我们假设我们维护一个事实商店,记录进入我们经纪商的所有交易。并且所有3个下游服务所有者都打电话给我们告诉我们他们已经丢失了过去3天的所有数据。 FTP下载落后3天,大型机落后3天,所有交易都落后3天。

    因为我们有事实存储,理论上我们可以从一定时间到特定时间重播所有这些消息。在我们的例子中,从3天前到现在。下游服务可能会被赶上。

    这个例子可能看起来有点过分,但我试图传达一些非常特别的东西:事实是要记录的重要事项,因为这是我们将要使用的重点我们的架构与重复战斗。

    Fact store如何帮助我们处理重复的消息

    如果您在持久层上实现事实存储,该持久层为您提供CAP theorem的CA部分,一致性和可用性,您可以执行以下操作:

    一旦收到来自队列的消息,您就可以在您的实体店中检查您之前是否已经看过此消息,如果有,是否已将此消息锁定,以及待定状态。在我的例子中,我将使用MongoDB来实现我的事实存储,因为我对它很满意,但是其他各种数据库技术应该能够处理它。

    如果该事实尚不存在,则会将其插入到事实存储中,具有挂起状态和锁定到期时间。这应该使用原子操作来完成,因为你不希望这发生两次!这是您确保服务idempotence

    的地方

    快乐案例 - 大部分时间都会发生

    当Fact存储回到您的服务,告诉它事实不存在,并且锁定已创建时,服务会尝试执行此操作。完成后,删除SQS消息,并将事实标记为已完成。

    重复留言

    这样当消息传来时会发生什么,并且它不是重复的。但是,让我们看一下重复的消息何时进入。服务选择它,并要求事实存储用锁来记录它。事实商店告诉它它已经存在,并且它被锁定了。该服务忽略该消息并跳过它!一旦消息处理完成,由另一个工作人员完成,它将从队列中删除此消息,我们不会再看到它。

    灾难案例 - 很少发生

    那么当一个服务在商店中第一次记录这个事实,然后在一段时间内获得一个锁定但是会倒下时会发生什么? SQS会再次向您发送一条消息,如果它被拾取,但在从队列中提供后的一段时间内没有被删除。这就是为什么我们编码我们的事实存储,使服务在有限的时间内保持锁定。因为如果它失败了,我们希望SQS在稍后的时间将消息呈现给服务或其他实例,允许该服务假设事实应该再次合并到状态(已执行)中。

答案 1 :(得分:4)

没有API级别的方法可以防止将重复的消息发布到SQS队列。你可能需要在应用程序级别处理这个问题。

您可以使用DynamoDB表来存储等待被抓取的域名,并且如果它们不在DynamoDB中,则只将它们添加到队列中。

答案 2 :(得分:2)

由于您无法阻止SQS发送重复的邮件,因此您必须执行此操作。一种简单的方法是使用Apache Camel的幂等消费者,请参阅http://camel.apache.org/idempotent-consumer.html

答案 3 :(得分:2)

根据AWS Docs,Exactly-Once Processing提供了避免重复消息的方法。

  

与标准队列不同,FIFO队列不会引入重复   消息。 FIFO队列可帮助您避免将重复项发送到队列。如果   您在5分钟重复数据删除中重试SendMessage操作   在时间间隔内,Amazon SQS不会在队列中引入任何重复项。

如果您的队列是FIFO队列并且已启用基于内容的复制,则可以使用此功能来避免重复数据删除间隔期间出现重复消息。有关详情,请参阅此section和以下链接。

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-sqs-queues.html#cfn-sqs-queue-contentbaseddeduplication

答案 4 :(得分:2)

Amazon SQS引入具有精确处理能力且标准队列价格更低的FIFO队列

  

使用Amazon SQS消息重复数据删除ID消息   重复数据删除ID是用于对已发送消息进行重复数据删除的令牌。   如果发送了具有特定邮件重复数据删除ID的邮件   成功发送的所有具有相同重复数据删除ID的消息   被成功接受,但在5分钟内未交付   重复数据删除间隔。

Amazon SQS Introduces FIFO Queues

Using the Amazon SQS Message Deduplication ID

答案 5 :(得分:0)

您可以使用VisibilityTimeout参数:

var params = { 
    VisibilityTimeout: 20,
    ...
};

sqs.receiveMessage(params, function(err, data) {});

参考:https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html

VisibilityTimeout —(整数)
持续时间(以秒为单位) 接收到的消息在之后的后续检索请求中被隐藏 被ReceiveMessage请求检索。

答案 6 :(得分:0)

只有当 AWS 没有引入更新来检测重复消息时,我才会做这样的事情.. 只是大声思考,将所有内容推送到 Redis 的 TTL 为 15 分钟(或任何适合的时间段)用例)一旦请求到达您的网关/您的环境并使您的生产者在发送/将消息放入队列,如果消息存在,则将响应返回给客户端,说明发现重复,如果不允许将其推送到队列中。 Redis 需要真正的 HA。