2014-04-24 91 views
21

防止Amazon SQS中的重複郵件的最佳方法是什麼?我有一個等待被抓取的SQS域名。在我向SQS添加新域之前,我可以檢查保存的數據以查看它是否最近被抓取,以防止重複。如何防止重複的SQS消息?

問題在於還沒有被抓取的域。例如,如果隊列中有1000個域尚未被抓取。任何這些鏈接都可以再次添加,並且一次又一次。這將我的SQS膨脹爲數以十萬計的大部分重複的消息。

我該如何預防?有沒有辦法從隊列中刪除所有重複項?還是有一種方法可以在我添加之前在隊列中搜索消息?我覺得這是SQS任何人都必須經歷的問題。

我可以看到的一個選擇是,如果我在將域添加到SQS之前存儲了一些數據。但是如果我必須將數據存儲兩次,那麼首先會破壞SQS的使用。

+0

[中SQS隊列使用了衆多消費者]的可能的複製(http://stackoverflow.com/questions/37472129/using-many-consumers-in-sqs-queue) – Krease

+1

AWS現在提供[FIFO隊列(HTTP: //docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/FIFO-queues.html),它提供「精確的一次處理,但每秒只限於300次事務」。 – bishop

回答

15

正如上面提到的其他答案,您不能防止來自SQS的重複消息。

大多數情況下,您的郵件將交給您的消費者一次,但您在某個階段會出現重複。

我不認爲這個問題有一個簡單的答案,因爲它需要提出一個適當的架構來處理重複,這意味着它在自然界是冪等的。

如果分佈式架構中的所有工作者都是冪等的,那很容易,因爲您不需要擔心重複。但實際上,這種環境並不存在,在某種程度上無法應對。

我目前正在研究一個需要我解決這個問題的項目,並且提出了一個處理它的方法。我認爲這可能會使其他人分享我的想法。它可能是一個很好的地方,可以對我的想法獲得一些反饋。

事實存儲

這讓他們收集理論上可以重播重現所有受影響的下游系統相同狀態的事實,開發服務心中有數。

例如,假設您正在爲股票交易平臺構建消息代理。 (我之前實際上曾在這樣一個項目上工作過,這很可怕,但也是一個很好的學習經歷。

現在,讓我們說,是交易進來,並有3個系統感興趣:

  1. 一個老派的主機,其需要保持更新
  2. 是整理所有的行業和系統與FTP服務器
  3. 記錄該交易,並重新分配股份的新主人

這是一個有點令人費解的服務於合作伙伴分享,I K現在,但這個想法是,一個消息(事實)進來,有各種分佈式下游影響。

現在我們假設我們維護一個事實存儲庫,記錄所有進入我們經紀人的交易。而且所有3家下游服務業主都打電話告訴我們,他們已經丟失了過去3天內的所有數據。 FTP下載3天后,大型機落後3天,所有交易都落後3天。

因爲我們有事實存儲,所以理論上我們可以重放所有這些消息,從某個時間到某個時間。在我們的例子中,這將從3天前到現在。下游服務可能會受到影響。

這個例子可能看起來有點過頭了,但我試圖傳達一些非常特別的東西:事實是重要的事情要跟蹤,因爲這是我們將在我們的架構中用來戰鬥重複的地方。

的事實存儲如何幫助我們提供您實現一個持久層,讓你的CAP theorem,一致性和可用性的CA部分的事實存儲重複的消息

,您可以執行以下操作:

一旦收到來自隊列的消息,就會檢查事實存儲庫中是否已經看到過此消息,並且如果有的話,此時是否鎖定以及處於未決狀態。在我的例子中,我將使用MongoDB來實現我的事實存儲庫,因爲我對它很滿意,但其他各種數據庫技術應該能夠處理這個問題。

如果該事實尚不存在,則將其插入事實存儲中,具有掛起狀態和鎖定過期時間。這應該使用原子操作來完成,因爲您不希望這種情況發生兩次!這是您確保您的服務idempotence的地方。

快樂的情況下 - 偏偏大部分的時間

當事實存儲又回到了你的服務告訴它其實並不存在,而且一鎖被創建,該服務將嘗試做的工作。完成後,將刪除SQS消息,並將事實標記爲已完成。

重複消息

所以這時候一個消息來通過,這不是一個重複發生的事情。但是讓我們看看重複的消息何時進來。服務會將其提取出來,並要求事實存儲庫用鎖來記錄它。事實存儲庫告訴它它已經存在,並且它已被鎖定。該服務忽略消息並跳過它!一旦消息處理完成,另一名工作人員將從隊列中刪除此消息,並且我們不會再看到它。

災害的情況下 - 很少發生

當一個服務記錄了在店裏第一次的事實會發生什麼,然後得到一段時間的鎖定,但倒了?那麼SQS將再次向您顯示一條消息,如果它被提取,但在從隊列中提供一定時間內沒有被刪除。這就是爲什麼我們編寫我們的事實存儲庫,以便服務在有限的時間內保持鎖定。因爲如果崩潰了,我們希望SQS在稍後時間向服務或其另一個實例提供消息,從而允許該服務假定事實應該再次併入狀態(執行)。

+0

謝謝你分享你的方法 –

+0

沒問題,如果你採用類似的方法,並遇到問題,讓我知道。我可能會提供幫助。 – hendrikswan

+1

真棒回答!輕微的挑剔:我會說在**快樂案例**中,您應該將事實標記爲已完成,然後_刪除SQS消息。如果事實已經標記爲已完成(不等待原始處理程序執行此操作),則我還會建議更新**重複消息**的情況以刪除消息。 –

2

沒有API級別的方法來防止重複消息發佈到SQS隊列。您恐怕需要在應用程序級別處理此問題。

您可以使用DynamoDB表來存儲等待被抓取的域名,並且僅當它們不在DynamoDB中時纔將其添加到隊列中。

+2

但是,如果我這樣做,爲什麼甚至使用SQS呢?爲什麼不直接讓應用程序直接從DynamoDB讀取?也許我誤解了SQS的使用,但如果我仍然需要將所有數據存儲在數據庫中,我覺得SQS會失去它的價值和意義。對我而言,我想使用SQS的原因是不需要將數據寫入數據庫。 –

+1

這是一個架構決定。 SQS(或任何排隊系統)在允許應用程序之間的異步通信以及使多個消息使用者使用來自多個生產者的消息方面非常出色。示例將在Web層和批量工作人員之間進行。數據庫並非設計用於這些類型的通信,而是需要額​​外的工作。但DB擅長分享獨立工作者或應用程序之間的狀態。在你的用例中,也許一個數據庫就足夠了。 –