訊息佇列全面大指南 - sudhir

banq發表於2021-01-08

訊息佇列基礎概念的指南,以及它們如何應用於當今流行的排隊系統。
在本指南中,我們將討論:
  • 什麼是訊息佇列及其歷史記錄。
  • 為什麼它們有用,以及在推理時要使用哪些心理模型。
  • 交付保證了排隊系統的語義(至少一次,最多一次和完全一次語義)。
  • 排序和FIFO保證以及它們如何影響排序,並行性和效能。
  • 扇出和扇入的模式:將一條訊息傳遞到多個系統,或將訊息從許多系統組合成一個。
  • 有關當今可用的許多流行系統的優缺點的說明。

 

什麼是訊息佇列?
訊息佇列是在兩個系統之間傳輸資訊的一種方式。此資訊(一條訊息)可以是資料,後設資料,訊號或這三者的組合。傳送和接收訊息的系統可以是同一臺計算機上的程式,同一應用程式的模組,可能在不同計算機或技術堆疊上執行的服務或完全不同種類的系統,例如將資訊從軟體傳輸到電子郵件或手機網路上的SMS。
每條訊息的內容將完全由您的應用程式的體系結構及其用途來決定-因此,在本指南的其餘部分中,我們無需關注訊息中的內容-我們更關注訊息的方式從其起源的系統(生產者,源,釋出者或傳送者)到達本應到達的系統(消費者,訂戶,目的地或接收器)。
 

為什麼我們需要它們?
我們需要訊息佇列,因為沒有系統存在或無法孤立地工作-所有系統都需要以它們都能理解的結構化方式與它們都可以處理的受控方式與其他系統通訊。任何非平凡的流程都需要一種在流程的各個階段之間移動資訊的方法。任何工作流程都需要一種在該工作流程的各個階段之間移動中間產品的方法。訊息佇列是處理此移動的好方法。有很多方法可以使用API​​呼叫,檔案系統或許多其他對事物自然順序的濫用來獲取這些訊息。但是所有這些都是訊息佇列的臨時實現,有時我們拒絕承認我們需要這些實現。
訊息佇列的最簡單思維模型是一個很長的管,您可以將它扔進去。您將您的訊息寫在一個球上,將其滾動到管子中,另一端收到某人或其他東西。此模型有很多有趣的好處,其中包括:

  • 我們不必擔心接收訊息的人或物件是什麼–發件人不必擔心要承擔的責任。
  • 我們不必擔心接收方何時接收訊息。
  • 我們可以按照自己喜歡的速度將任意數量的訊息放入管道(假設我們有一個無限長的管道)。
  • 接收者永遠不會受到我們行動的影響-他們將以他們希望的任何速率拉出任意數量的訊息。
  • 無論是傳送還是接收方所關心的是如何的其他作品。
  • 傳送方和接收方都不關心對方的容量或負載。
  • 兩個系統都不關心另一個系統的位置–它們可能位於或不在同一臺計算機,網路,大陸或同一星球上。

所有這些優點(甚至還不是一個詳盡的清單)在軟體開發中都具有非常重要的優點-它們的共同點是去耦。一個系統在職責,時間,頻寬,內部工作方式,負載和地理位置方面與另一個系統是分離的。解耦是任何分散式或複雜系統中非常需要的部分-系統各部分之間的解耦越多,獨立構建,測試,執行,維護和擴充套件它們就越容易。
大多數系統也可以與其他外部或第三方系統進行互動-如果我們構建購物網站,則可能與支付處理器進行互動,並且假設我們嘗試在每次使用者點選時直接與支付處理器進行通訊。如果我們的系統承受重負載,那麼我們還要使另一個系統承受相同的負載。反之亦然,如果我們的付款服務提供商需要向我們傳送數百萬條有關我們過去付款狀態的資訊,那麼我們的系統就更好了。
現在將兩個系統耦合。一個系統做出的決定和動作會對另一個系統產生重大影響,因此在做出每個決定時都必須考慮到兩者的需求。將足夠多的其他系統(例如物流或交付系統)新增到組合中,我們很快就會陷入癱瘓狀態,從而很難做出任何決定。如果一個系統發生故障,則其他系統也有效地發生故障,而它們本身沒有故障。
如果我們想將這些系統中的任何一個切換為另一個系統,例如新的付款處理器或交付系統,也會遇到麻煩。我們必須在應用程式的多個位置進行深刻的更改,構建程式碼以在多個提供程式之間拆分訊息更加困難—我們可能需要使用比率來負載均衡它們或按地理位置拆分它們;或根據每個提供商的可用性或成本在它們之間動態切換。
訊息佇列提供瞭解決許多這些問題的解耦功能。如果我們在兩個需要相互通訊的系統之間建立一個佇列,那麼他們現在就可以繼續工作而不必擔心彼此—我們將針對任何系統的訊息放入佇列中,我們期望獲得資訊也可以透過另一個佇列來訪問我們。現在,我們有了明確的要點,可以在這些要點上新增規則或進行所需的更改,而無需系統知道或關心不同之處。
 

那麼有什麼收穫呢?
訊息佇列是計算的聖盃嗎?他們解決了世界上所有的問題嗎?不,當然不。在很多情況下,我們可能不想使用它們。而且我們當然不希望僅僅因為我們有一個容易使用的佇列就認為使用佇列很有趣。有些系統真的很簡單,只是不需要它—訊息佇列是降低通訊系統複雜性的一種方法,但是兩個通訊系統總是比一個不必通訊的系統更為複雜。如果您的系統非常簡單,不需要與其他任何人進行通訊,那麼就沒有任何理由排隊。
也有一些系統可以互相通訊,但是這些通訊所增加的複雜性微不足道,因此不必擔心。從某種意義上說,它們都需要協同工作才能正常執行,或者更經常地,系統已經耦合。一個真正常見的示例是應用伺服器和資料服務(在OLTP系統中)。將它們與佇列解耦沒有多大意義,因為如果沒有另一個的直接參與,這兩個都無法做任何有用的事情。
然後,還要考慮效能-將兩個系統在時間和負載方面分離開來,以便它們可以按照自己的進度處理資訊-但我們當然不希望這種情況發生在對效能敏感的應用程式或時間系統。佇列可以幫助我們同時處理更多的工作(接收者可能有許多並行處理您傳送的訊息的工作),但會消除我們對每項工作所需的確切時間的保證。如果可預測性比吞吐量更重要,那麼最好不要排隊。
使用佇列可能會增加處理每個所花費的時間個人資訊,但會允許你在處理跨不同的,同時還有更多的訊息電腦,讓你的每分鐘或一小時,或處理的訊息總數的吞吐量將增加。
如果確實有多個需要通訊的系統,並且通訊需要持久(如果將訊息放入佇列中,我們要確保訊息傳遞系統不會“忘記”它)並且解耦後,訊息佇列必不可少。
 

訊息傳遞語義學
建立訊息佇列的人將聲稱他們的系統提供了三種傳遞保證之一—您放入佇列中的每條訊息都將被傳遞:

  • 至少一次。
  • 最多一次。
  • 恰好一次。

我們正在使用的保證將對我們系統的設計和工作產生巨大影響,因此讓我們將它們中的每一個拆開。
  • 至少一次

這是最常見的交付機制,也是最簡單的推理和實施機制。如果我有一條要給您的訊息,我會給您閱讀,並一遍又一遍,直到您確認為止。而已。在至少一次執行的系統中,這意味著當您從佇列中收到一條訊息並且不刪除/確認該訊息時,以後您將再次收到它,並且將一直接收到該訊息為止明確刪除/確認它。
這是最常見的保證,其原因是它很簡單,並且可以100%地完成工作-在任何情況下都不會丟失訊息。即使接收器在確認訊息之前崩潰了,它也只會再次接收相同的訊息。不利的一面是,作為接收方的您需要計劃多次接收同一條訊息,即使您不一定遇到崩潰也是如此。這是因為至少提供一次是保護排隊服務也不會丟失訊息的最簡單方法-如果您的確認沒有透過網路到達排隊系統,則訊息將再次傳送。如果繼續確認存在問題,該訊息將再次傳送。如果排隊系統在重新啟動之前可以正確跟蹤已傳送給您的內容,該訊息將再次傳送。在任何方面出現任何問題的情況下,再次傳送訊息的簡單補救措施就是使此保證如此可靠。
但是訊息重複/重複是一個問題嗎?這實際上取決於您和您的應用程式或用例。例如,如果該訊息是時間戳和度量,則接收一百萬個副本沒有問題。但是,如果您根據訊息來轉移資金,那肯定是一個問題。在這種情況下,您將需要在接收端擁有一個事務(ACID)資料庫,並可能將訊息ID記錄在唯一索引中,以使其無法重複。這稱為使用冪等標記或邏輯刪除-當您對訊息進行操作時,您通常會在與執行操作本身相同的資料庫事務中儲存唯一的永久標記來跟蹤您的操作。即使訊息重複,也會阻止您再次重複該操作。
如果您處理重複,或者您的訊息天生就可以抵抗重複,則可以說您的系統是冪等的。這意味著您可以安全地處理多次收到相同的訊息,而不會破壞您的工作。這通常也意味著您可以容忍發件人多次傳送同一條訊息-請記住,發件人在傳送訊息時通常也將至少一次執行一次操作。如果發件人無法記錄他們已傳送特定訊息的事實,則只需再次傳送即可。然後,發件人有責任確保在重新傳送訊息時使用相同的冪等令牌。
  • 最多一次

這是一種非常罕見的語義,用於重複性極高的爆炸性訊息(或訊息如此根本不重要)以至於我們不希望根本不傳送訊息,而不是傳送兩次。最多一次表示排隊系統將嘗試一次將訊息傳遞給您,但僅此而已。如果您收到並確認該訊息一切正常,但是如果您不滿意,或者出現任何錯誤,則該訊息將永遠丟失-這是因為排隊系統在嘗試將訊息傳送給您之前花了很大的功夫將其記錄下來(如果該訊息具有極高的爆炸性),或者根本沒有費心去記錄該訊息,而只是像路由器傳遞UDP資料包一樣傳遞訊息。
這種語義通常在充當無狀態資訊路由器的訊息傳遞系統中起作用。或在重複訊息具有破壞性的情況下,如果出現任何故障,則必須進行調查或對帳。
  • 恰好一次

這是訊息傳遞的聖盃,也是許多蛇油的源泉。這意味著保證每條訊息都只傳送一次,處理一次,最多也不會減少。每個構建或使用分散式系統的人在生活中都會想到“這有多難?”,然後他們要麼(1)瞭解為什麼這是不可能的,找出冪等性,然後至少一次使用,或者(2)他們嘗試建立一個半確定的“完全一次”系統,並以高價將其出售給尚未弄清(1)的人。
不可能一次傳送的原因有兩個基本事實:
  1. 發件人和收件人不完善
  2. 網路不完善

如果您深入思考問題,那麼很多事情都會出錯:
  1. 發件人可能無法記錄(他們忘記了)他們已經傳送了訊息
  2. 網路呼叫傳送訊息可能失敗
  3. 訊息系統的資料庫可能無法記錄訊息
  4. 訊息系統已記錄訊息的確認可能無法透過網路到達發件人
  5. 發件人可能無法記錄訊息傳遞系統已收到訊息的確認

假設在傳送訊息時一切順利-當訊息傳遞系統嘗試將訊息傳遞給接收者時:
  1. 該訊息可能無法透過網路到達接收者
  2. 接收者可能無法在其資料庫中記錄訊息
  3. 來自接收者的確認可能無法透過網路到達訊息系統
  4. 訊息系統的資料庫可能無法記錄訊息已傳遞

考慮到所有可能出問題的地方,任何訊息傳遞系統都不可能保證一次傳送。即使訊息傳遞系統完美無瑕,大多數可能出問題的地方還是在訊息傳遞系統之外或在互連網路中。某些系統確實嘗試使用“僅一次”這個短語,通常是因為它們聲稱其實現絕不會遇到上述任何訊息傳遞系統問題-但這並不意味著整個系統都擁有一次精確的語義魔術,即使宣告確實是真實的。這通常意味著排隊系統具有某種形式的排序,鎖定,雜湊,計時器和冪等性令牌,這將確保它永遠不會重新傳遞已經被刪除/確認的訊息,但是不會。
大多數優秀的訊息傳遞系統工程師都理解這一點,並將向使用者解釋為什麼這種語義不可行。處理訊息的更簡單,更可靠的方法是回到基礎知識,並在傳送,接收和排隊過程的每個點上至少一次採用冪等度量:如果一開始您不成功,請重試,重試,重試...
 

有序與並行
在傳遞語義之後,人們心中的另一個常見問題是“為什麼我們既不能同時處理訊息,又要確保我們按順序處理訊息?”。不幸的是,這是邏輯專制強加給我們的另一個權衡。依次進行工作和同時進行多項工作總是相互衝突的。大多數訊息佇列系統都會要求您選擇一個-AWS SQS是透過將並行性放在優先順序之上來開始的;但最近又引入了一個單獨的FIFO(先進先出)排隊系統,該系統保持嚴格的順序排序。在兩者之間做出選擇之前,讓我們先探討一下區別是什麼以及為什麼根本需要區別。
回到我們較早的隱喻佇列(長的管子,我們將寫在球上的訊息滾動到其中),我們可能想象管子比單個球要寬一點。實際上,球在管內根本不可能超車或彼此透過,因此接收器發出這些訊息的唯一方法是按照它們被放入的順序一個接一個地傳送。這保證了嚴格的排序,但是放置牢固對我們接收器的限制。有可能只有一個 代理在正在處理每條訊息的接收方,如果多於一條,則不能保證訊息是按順序處理的。因為每個新代理都可以獨立處理每個訊息,所以它們可以隨時完成並從下一條訊息開始。如果是兩個代理,A和B,並且代理A接收到第一個訊息,代理B接收第二個訊息;甚至在代理A完成處理第一條訊息之前,代理B即可完成第二條訊息的處理並從第三條訊息開始。儘管嚴格按照放入順序從佇列中接收訊息,但是如果存在多個接收代理,則無法說出訊息將按照該順序進行處理。
代理可以使用某種分散式 相互協調,但這基本上與只有一個代理相同-該鎖將只允許一個代理在任何給定時間工作。這也意味著一個代理崩潰將導致死鎖,而無需完成任何工作。
訊息傳遞系統保證命令的一種方式是,試管拒絕發出下一個球,直到並且除非接收到的最後一個球被破壞(最後一條訊息已被刪除/確認)為止。這通常是FIFO佇列會執行的操作-僅在確認或刪除最後一個訊息後才提供下一條訊息-但這意味著即使只有N個代理在等待,一次也只能工作一個代理從佇列中接收訊息。
有時,這正是我們想要的。當我們只需要與單個代理人打交道時,某些操作就更容易有效地控制,例如對金融交易執行規則;遵守限速; 或通常假設將始終按順序處理其格式已設計好的訊息。但是,這些“好處”中的許多並非真正來自決定使用FIFO排序的情況-在任何情況下,如果我們有N個接收器,它們必須以某種方式彼此協調工作,則將受益於N = 1的特殊情況。關鍵要點是要求有保證的順序意味著我們必須一次只在一個接收器上順序處理訊息。
這種限制也給排隊系統帶來了巨大壓力,因此您會發現FIFO佇列通常比並行佇列更昂貴且容量更少。這是因為相同的邏輯限制也適用於排隊系統的內部實現-大多數工作需要限制在單個代理或伺服器上,並且該系統必須保持可靠。任何增加冗餘的工作都需要在主伺服器和備用服務之間進行同步協調,以維持訂購保證。在AWS SQS中,FIFO佇列的成本比並行佇列高2倍左右,並且在需要嚴格的FIFO排序時,每秒被約束到幾百條訊息。
因此,推進FIFO訊息佇列的唯一方法是接受整個訊息處理體系結構將具有固有的速度限制。許多系統將在佇列內支援組標題,以表示我們要嚴格排序的訊息-我們可能會說,“付款”標題下的所有訊息都必須是FIFO,而“順序”標題下的所有訊息都必須是FIFO,但是它們彼此之間不需要是FIFO。這允許在佇列內進行一些並行化(例如,使用兩個FIFO管而不是一個),但是我們需要記住,每個組標題內的訊息頻寬仍將受到限制。
平行!=隨機
這是否意味著並行佇列中的排序是完全隨機的?有時候是的,但通常不是。在SQS中,比喻更多的是,沒有從傳送者到接收者的一根管子,而是多根管子。他們也可能在此過程中相互分支或加入。這並不意味著您以任何方式有意地隨機分配了要滾動的訊息的順序-在大量訊息中,您仍然希望通常較早的訊息早於較晚的訊息被接收。這是最大的努力排序,需要付出一定的努力來保持排序的完整性,但是由於在邏輯上已經不可能了,因此對於系統而言,這並不是一個重要的優先事項。這也允許像SQS這樣的訊息傳遞系統擴充套件到幾乎無限的容量-因為如果要滾動很多訊息,則排隊系統可以簡單地新增更多的管道。可以想象,這將同時支援任意數量的接收者,也支援任意數量的傳送者。這種簡單性使SQS可以擴充套件到令人難以置信的數字,包括一個佇列中有超過2500億條訊息等待消費的情況,而接收器每秒讀取和確認一百萬條訊息。而這僅僅是一個客戶操作的一個佇列。
看起來他們對FIFO有嚴格要求的大多數問題通常可以使自己具有並行性和無序交付,但需要一點點創造力。發件人在訊息中新增時間戳是一種幫助解決此問題的方法,例如,在訊息為度量標準的情況下,只有最後一個很重要。在更具事務性的系統中,發件人通常可以在訊息中新增單調遞增的計數器
如果這無法實現,那麼我們也許可以根據訊息的內容進行處理:例如,如果要傳送訊息的檔案所佔的百分比,則看到41%,42%和43%始終表示當前值為43%-即使我們認為它們分別是41%,43%和42%,也是如此。
雖然更改系統以適應我們使用的工具通常是一個壞主意,但是設計訊息以允許無序傳遞和冪等使系統總體上更具彈性,同時還允許使用更多的並行訊息傳遞系統,通常可以節省成本時間,金錢和大量運營工作。
 

Fan Out 扇出/ 扇入In
在構建分散式系統時,通常需要將同一條訊息傳送給多個接收者,除了通常的訊息接收者之外,我們還經常希望將同一條訊息傳送到其他地方,例如存檔,稽核日誌(為了遵守法規和安全檢查)或我們儀表板的分析器。如果您使用的是具有許多服務的事件驅動的體系結構,則可能需要在應用程式中使用單個事件匯流排,其中釋出到該事件匯流排中的所有訊息都會自動傳送到所有服務。這稱為扇出問題,其中一個生產者的訊息需要傳達給許多消費者。
相反的問題是,單個接收者負責讀取釋出到多個佇列中的訊息的情況也很常見-在我們上面所考慮的示例中,正在歸檔所有訊息或建立稽核日誌的接收器可能會接收到在一個佇列中生成的所有訊息。組織,每一個佇列。在服務體系結構中,通常還具有像通知那樣單獨處理的功能-因此,通知系統可能需要接收有關新確認的訂單,付款失敗,運輸成功等訊息。這是一個扇入式問題,其中來自許多生產者的訊息需要到達同一消費者。
如果所有生產者都將其訊息直接放入佇列中,這將是一個非常難以解決的問題-我們必須以某種方式攔截我們的佇列,並將訊息可靠地複製到多個佇列中。構建,配置和維護該總機根本不值得花費時間或精力,尤其是當我們只可以使用Topic主題時。
想象主題topic的一種方法是,它們類似於您在學校或辦公室的公告板上看到的標題。生產者在板上的特定主題下發布訊息,對此主題感興趣的每個人都將看到該訊息。訊息傳遞系統向感興趣的接收者傳送訊息的最常見方式是HTTP(S)請求,有時也稱為Webhook。在基於推送的系統(如HTTP請求)中,無論訊息是否就緒,都會將其推送到接收器中。這重新介紹了我們之前要避免的耦合,我們不希望接收器在短時間內跨越數十/數百/數千/百萬個webhooks的巨大負載而崩潰的情況。同樣,這裡的答案是僅使用訊息佇列以無論主題生成的速率吸收主題中的訊息。然後,接收者可以按照自己的速度處理它們。
將訊息從一個主題自動複製到一個或多個佇列中並不是嚴格意義上的訊息佇列功能,而是一項補充功能-大多數功能齊全的訊息傳遞系統都將提供一種實現此目的的方法。生產者仍將像往常一樣繼續將訊息放在一個地方,但這將是一個主題,在內部訊息將被複制到多個佇列,每個佇列將由各自的接收者讀取。
如果您使用的是Apache Kafka之類的其他系統,那麼您也會在其中看到類似的概念-您將釋出訊息的主題稱為主題,並且任何數量的使用者都可以閱讀該主題中的所有訊息。Google的釋出/訂閱系統還整合了主題和佇列。
這些場景的這種結合非常普遍,以至於有一個簡單的,完善的模式可以處理它:

  • 將每個訊息釋出到一個適當的主題。
  • 為每個接收者建立一個佇列。
  • 將每個接收者的佇列連結到接收者感興趣的主題。

由於通常可以為任意數量的主題預訂佇列,因此在接收方不需要額外的管道即可處理來自多個主題的訊息。當然,可以有任意數量的訊息佇列訂閱單個主題。這種設定既支援扇出也支援扇入,並且使您的體系結構在將來可以擴充套件和更改。
 

毒藥和死信
聽起來很病態,當設定系統與多個其他系統對話時,肯定會發生錯誤。常見的問題是,訂閱者被掛接到接收來自其不瞭解的主題的訊息的訊息,而該訊息格式是其無法理解的。怎麼了?訂戶是否忽略該訊息?還是確認/刪除它?忽略它不會錯,因為訊息只會在一次至少一次的系統中不斷反覆出現?但是,刪除/確認我們未處理的訊息是否更糟?在我們找到用樹木倒下的樹木製成的哲學書籍之前,我們可能需要配置一個死信佇列在我們的佇列中。這是許多佇列系統提供給我們的功能,如果系統看到一條訊息被髮送出去以進行重複處理(每次失敗),它將被移到一個特殊的佇列中,稱為死信佇列。我們希望將此佇列掛接到某種警報上,因此我們將很快知道發生了什麼奇怪的事情。
更糟糕的情況是,該訊息以某種方式具有爆炸性-可能是以XML格式而不是JSON格式,或者包含使用者生成的內容,其中包含格式錯誤的輸入攻擊,這會導致解析程式碼崩潰……您的訂閱者吞下了一個毒丸。這種藥丸到達使用者時會發生什麼情況,很大程度上取決於您的技術堆疊,因此不用說,您要仔細考慮使用者程式碼中的錯誤處理和異常。好訊息是,如果您配置了一個死信佇列,則僅靜默失敗可能是一個不錯的選擇。毒藥最終將出現在死信佇列中並可以進行檢查。即使有害訊息使您的訂戶崩潰,使用程式管理器執行自動重啟通常也足以重試該訊息多次,以至於將其移至死信佇列。但是,您確實需要確保沒有安全隱患,並且請記住,這是一種簡單的DoS攻擊
請記住,無論是根據訊息的結構是否符合預期的方式以及是否是預期的收件人,都要始終驗證傳入的訊息。
 

Q-List佇列產品列表
這是一些當前流行的訊息排隊系統的列表,並列出了到目前為止我們所看到的概念如何應用於它們中的每個。

  • AWS SNS和SQS

AWS執行兩項服務,它們相互整合以提供完整的訊息排隊功能。SQS服務是一個純粹的訊息佇列,它允許你建立一個佇列,傳送一條訊息,並收到一條訊息。而已。ReceiveMessageSQS佇列上的API是僅拉式的,因此,只要接收者準備好處理訊息,就需要呼叫它。有一個WaitTimeSeconds選項可以阻止呼叫等待訊息的時間長達20秒,因此一種有效的模式是ReceiveMessage在等待20秒的情況下以無限迴圈輪詢API。

SNS的整合附帶了主題和扇出/扇入功能,該功能可用於構建主題。這允許將訊息釋出到主題(而不是佇列)中。然後,您可以將任意數量的SQS佇列預訂到一個主題中,因此釋出到該主題的訊息可以快速複製到所有預訂的佇列中,而無需支付額外費用。您將需要開啟raw message選項,該選項使將訊息釋出到SNS主題中實際上等同於將訊息釋出到SQS佇列中-不會對訊息進行任何轉換或打包。
SQS和SNS都是完全託管的服務,因此無需維護伺服器或安裝軟體。根據您傳送和接收的訊息數向您收費,並且AWS會處理擴充套件到任何負載的情況。
FIFO選項在SNSSQS上可用,具有不同的定價和容量保證。AWS使用術語訊息組ID來表示所有訊息都是FIFO的組標題。透過在刪除前一條訊息之前不給出下一條訊息,來按順序傳遞組標題中的訊息。

  • Google Pub / Sub

Google提供釋出/訂閱服務作為其雲平臺的一部分,以處理整合服務中的訊息佇列和主題。主題的概念如您所願地存在,而佇列稱為subscription。如預期的那樣,將多個訂閱與一個主題相關聯會將訊息複製到所有相關聯的訂閱中。除了允許訂閱者輪詢或拉出訂閱中的訊息外,Pub / Sub還可以將訊息的Webhook樣式POST到您的伺服器,讓您透過成功返回狀態程式碼對其進行確認。
這也是一個完全託管的系統,例如AWS。您會根據傳送的郵件數向您收費,Google會根據需要擴充套件系統規模。它還具有SNS + SQS組合中沒有的一些功能,例如允許您使用時間戳和重播訊息來檢視歷史記錄。

FIFO功能存在於訂購金鑰的上下文中,該功能使您可以透過在確認前一條訊息之前不給您下一條訊息,從而確保按順序處理訂購金鑰中的訊息。

  • AWS EventBridge

來自AWS的新產品EventBridge提供了完全託管的事件匯流排—這是佇列和主題概念的一種變體,其中所有訊息都發布到一條匯流排中,沒有可見的主題分離。相反,每條訊息都需要根據一種標準格式進行結構化,該標準格式中應包含有關訊息主題的資訊。然後,匯流排將讀取該訊息,並將其路由到表示有興趣接收有關該主題的訊息的任何訂戶。從匯流排到訂戶的實際傳遞機制可以是SQS佇列,webhooks或許多其他特定於平臺的選項。這使得可以輕鬆地將事件匯流排作為可單獨配置的基於規則的總機進行管理,同時還允許使用簡單的外掛進行存檔,審計,監視,警報,重播等。
  • Redis流

Redis具有相對較新的Streams功能,非常適合訊息佇列。它透過動態建立主題並使用XADD命令向其中新增訊息來工作。使用可以直接從主題中讀取訊息XREAD,因此每個訂閱者可以維護自己的狀態(最後讀取的偏移量)以通讀訊息。為了避免每個訂戶必須保持其當前狀態,使用等效於佇列來建立使用者組更為有意義XGROUP CREATE。傳送到主題的每條訊息在每個消費者組中都變得獨立可用,然後可以透過進行訂閱XREADGROUP。可以使用確認訊息XACK。
Redis流使用可以自動生成或手動設定的時間戳自動進行FIFO排序。這也意味著一次只能由一個消費者代理處理訊息。為了解決此限制並與許多消費者代理並行工作,文件中描述了一個單獨的基於非流的模式,RPOPLPUSH主要用於將LPUSH訊息放入主題中,然後將RPOPLPUSH其放入其他列表中,每個列表代表一個佇列,或更準確地說它的工作正在進行中。LREM用於刪除/確認訊息。
Redis是一個開源系統,您可以自行安裝和維護或查詢託管主機。根據您所需要的系統的永續性,您可能希望找出要使用的最佳永續性機制。
  • apache kafka

Kakfa是一種流行的訊息代理,致力於生產者將訊息(稱為事件)釋出到主題的概念。使用主題內的分割槽鍵將主題中的事件劃分為多個分割槽,並在每個分割槽內維護FIFO順序。事件可以透過套接字流傳輸給使用者,也可以由使用者查詢以實現更分離的方法。對於不想維護狀態的消費者,適用消費者組的概念,與Redis Streams相同。一個消費群實際上是一個佇列,其中每釋出一個主題事件是適用於所有相關加工消費群。
Kafka是開源的,但是安裝和維護很複雜,因此適合大型專案和團隊。它根據您將事件分割成多個分割槽的程度進行擴充套件-分割槽越多,Kafka可以分配的工作就越多,並且每個分割槽的容量僅與負責管理它的伺服器一樣大。託管託管選項是可用的,但與託管服務(如SNS + SQS,Pub / Sub或RabbitMQ)相比,它們往往具有較高的基本成本。

  • RabbitMQ

RabbitMQ是流行的開源訊息代理,它支援多種協議,並直接支援主題和佇列的概念。RabbitMQ在最多一次和最少一次兩種模式下執行,最多一次是基於快速記憶體的模式,該模式在必要時偶爾將訊息寫入磁碟(可以在持久佇列或臨時佇列之間進行選擇)。如果您想要一個更可靠但更慢,至少一次的系統,則可以使用可靠性指南中描述的操作在釋出訊息時要求確認,並且需要強制確認閱讀它們時。預設情況下,佇列是FIFO,可以選擇使用確認來強制執行順序處理。
  • NSQ

NSQ在此列表中是第一個真正的分散式訊息佇列。它沒有連線到釋出或訂閱訊息的單一點:每個NSQ節點實際上都是一臺完整的伺服器,並與其他每個節點進行通訊。節點使您可以將訊息釋出到主題,並且每個主題都可以連結到一個或多個通道(相當於佇列)。釋出到某個主題的每條訊息都可以在其所有連結的渠道中找到。
NSQ預設為非永續性,至少一次,無順序的訊息傳遞,但是有一些配置選項可以進行調整。特別值得考慮的是,如果您的伺服器之間相互高度聯網,並且您想要一個沒有單點故障的系統。

  • NATS

NATS是一種高效能的分散式訊息傳遞系統,用於快速的記憶體中訊息傳遞。它支援基於主題的廣播(主題稱為主題),其中傳送給主題的所有訊息都傳送給所有訂戶代理。以及分散式佇列,其中佇列中的每個訊息都傳送到任何一個訂戶代理。沒有將主題連結到佇列的內建方法,但是應該可以以程式設計方式進行。
NATS透過提供流系統實驗性永續性系統,最多支援一次和至少一次交付。它還支援根據主題名稱模式訂閱多個主題,這使得扇入和多租戶操作更加容易。
當您需要高吞吐量的分散式系統時,NATS可以很好地工作-它也很容易執行,並且支援複雜的網路拓撲,例如使區域群集之間具有連線。

 

相關文章