防止Amazon SQS中重复邮件的最佳方法是什么?我有一个等待被抓取的域的SQS。在我向SQS添加新域之前,我可以检查已保存的数据以查看它是否最近被抓取,以防止重复。
问题在于尚未抓取的域。例如,如果队列中有1000个域尚未被爬网。任何这些链接都可以再次添加,一次又一次。这使我的SQS膨胀成数十万条主要是重复的消息。
如何防止这种情况?有没有办法从队列中删除所有重复项?或者有没有办法在添加消息之前搜索队列中的消息?我觉得任何有SQS的人都必须经历这个问题。
我可以看到的一个选项是,在将域添加到SQS之前,我是否存储了一些数据。但是如果我必须将数据存储两次,那么首先就会破坏使用SQS的重点。
答案 0 :(得分:32)
正如提到的其他答案,您无法阻止来自SQS的重复邮件。
大多数情况下,您的邮件会在一段时间内传递给您的某个消费者,但 会在某个阶段发生重复。
我不认为这个问题有一个简单的答案,因为它需要提出一个能够应对重复的适当架构,这意味着它本质上是幂等的。
如果分布式架构中的所有工作人员都是幂等的,那么这很容易,因为您不必担心重复。但实际上,这种环境并不存在,某种程度上无法处理它。
我目前正在开展一个项目,要求我解决这个问题,并想出办法来处理它。我认为在这里分享我的想法可能会让其他人受益。这可能是一个对我的想法得到一些反馈的好地方。
事实商店
开发服务是一个不错的主意,这样他们就可以收集理论上可以重播的事实,以便在所有受影响的下游系统中重现相同的状态。
例如,假设您正在为股票交易平台构建消息代理。 (我之前曾经参与过这样的项目,这很糟糕,但也是一次很好的学习经历。)
现在让我们说这些交易进来了,有3个系统感兴趣:
我知道,这有点令人费解,但想法是有一条消息(事实)进入,有各种分布式下游效应。
现在让我们假设我们维护一个事实商店,记录进入我们经纪商的所有交易。并且所有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和以下链接。
答案 4 :(得分:2)
Amazon SQS引入具有精确处理能力且标准队列价格更低的FIFO队列
使用Amazon SQS消息重复数据删除ID消息 重复数据删除ID是用于对已发送消息进行重复数据删除的令牌。 如果发送了具有特定邮件重复数据删除ID的邮件 成功发送的所有具有相同重复数据删除ID的消息 被成功接受,但在5分钟内未交付 重复数据删除间隔。
答案 5 :(得分:0)
您可以使用VisibilityTimeout参数:
var params = {
VisibilityTimeout: 20,
...
};
sqs.receiveMessage(params, function(err, data) {});
VisibilityTimeout —(整数)
持续时间(以秒为单位) 接收到的消息在之后的后续检索请求中被隐藏 被ReceiveMessage请求检索。
答案 6 :(得分:0)
只有当 AWS 没有引入更新来检测重复消息时,我才会做这样的事情.. 只是大声思考,将所有内容推送到 Redis 的 TTL 为 15 分钟(或任何适合的时间段)用例)一旦请求到达您的网关/您的环境并使您的生产者在发送/将消息放入队列,如果消息存在,则将响应返回给客户端,说明发现重复,如果不允许将其推送到队列中。 Redis 需要真正的 HA。