我正在為事件源服務使用參與者系統,我想了解為什么默認設定是最多一次傳遞,即從一個參與者到另一個參與者的訊息最多可能到達一次,但可能根本不會。我正在使用 Akka,但我知道這通常是演員模型實作的默認設定。
在我看來,最多一次交付很容易導致靜默失敗/資料損壞,并且至少一次交付的問題可以通過版本控制和標記訊息輕松解決。參考自 Akka 作業原理手冊:
(最多一次有效)特別是在偶爾丟失訊息不會使系統處于不一致狀態的情況下
我不明白如何從“偶爾丟失一條訊息沒什么大不了”到“丟失這條特定訊息沒什么大不了”的邏輯跳躍。在我看來:如果我實際上并不關心接收此訊息,為什么要發送它呢?
示例:我們正在構建一個系統來計算檔案中的單詞數。我們有一個actor(M)接收一個檔案,將它分成幾行,然后將每一行發送給一個子actor(C)來計算行中的單詞。M的狀態是檔案中有多少個單詞;它通過從其子 C 接收包含一行中單詞數的訊息來更新此狀態,并將該數字添加到總數中。
在最多一次傳遞中:如果子actor由于網路錯誤而丟失了訊息(如果它是實際actor的錯誤,則父級可以使用監督來修復),父級不會知道。它可以通過保存每個子actor的映射來跟蹤狀態以及它是否已完成并在回復進入時更新映射,但是如果我們使計算稍微復雜一些并且有來自子actor的可變數量的回復,這會得到很快失控。此外,我們剛剛建立了一個系統來確保訊息被接收,所以我們已經開始冒險走出最多一次傳遞的世界,特別是如果我們依賴它來將計算訊息重新傳遞給 C與只知道當前計數已損壞相反。
至少一次傳遞:我們可以給 M 的訊息一個序列 ID,并在父 M 中保留一個日志(C -> ID)。這讓我們知道如果訊息到達兩次,我們應該丟棄它。對我來說,如果孩子們的任務變得更復雜,這似乎更簡單,也更普遍可擴展。
uj5u.com熱心網友回復:
最多一次交付的最常見用例是對大量事件(例如,頁面瀏覽量)進行計數(或收集其他統計資料)。
對于這些應用程式來說,偶爾丟失一條訊息確實沒什么大不了的:如果您在給定的一分鐘內收到 999999 個事件而不是一百萬個,那么沒人會注意到,您也可以稍后重述您的統計資料而無需太多對任何事情都有影響。
過度計算將是一個更大的問題:當資料被重述時,數字會下降(通常不僅僅是 1,由于丟失或超時導致的過度計算很容易產生巨大的差異 - 見下文)。并且它不像您想象的那么容易避免(您建議在該“日志”中保留多少個 id,以及您會保留多長時間?這需要多少額外的記憶體?查找速度會有多慢?如何會處理組件重新啟動嗎?如何確保始終將重復事件發送到同一個組件實體?此外,您必須在系統的每個組件中實作此記錄保存)
但更重要的是,執行至少一次合同會使系統變得更加復雜,從而導致系統的可靠性大大降低。
在這個系統中,組件不能只是將訊息發送到管道中,然后忘記它。它必須等待從下游的每個組件接收 ack。如果 ack 沒有及時到達,它必須再次發送相同的訊息,并一直這樣做,直到收到 ack。想象一下,下游的某些組件由于某種原因(也許是記憶體利用率高)而運行緩慢。ack 超時,上游開始一次又一次地重新發送相同的事件。同時,新訊息不斷堆積,并且沒有被確認,因此它們也被怨恨。它呈指數級快速增長。這增加了已經出現問題的組件(使其更慢)以及所有其他組件的負載,迅速使已經很糟糕的情況變得更糟,并最終使整個系統崩潰。
uj5u.com熱心網友回復:
為什么它是默認值的簡短答案是:
- 在 at-most-once 之上實作 at-least-once 并不難(配對請求和回復的詢問模式大部分都是這樣),但是沒有很好的方法可以讓 at-most-once 脫離 at -least-once(例如,不產生 at-least-once 的成本)
- 最多一次意味著或多或少一件事,而不管背景關系如何,而至少一次意味著實際上是無限數量的事情(例如,在沒有回復的多長時間后,行程可以決定發送失敗,以及多少次應該重試嗎?)...沒有合理的默認含義,并且很有可能給定的應用程式/服務甚至沒有合理的默認值。
請注意,at-least-once 至少會導致與 at-most-once 一樣多的不一致性,尤其是在處理非冪等性并且添加冪等性的通用方法確實很重量級時;相反,在更高級別(以及在需要時)設計冪等性通常能夠輕得多。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/407321.html
標籤:
