Akka消息傳送可靠性 23

原文:https://doc.akka.io/docs/akka/2.5/general/message-delivery-reliability.htmlhtml

Akka可幫助您構建可靠的應用程序,這些應用程序在一臺計算機中使用多個處理器核心或分佈在計算機網絡中。使這項工做的關鍵抽象是你的代碼單元 -Actors之間的全部交互都是經過消息傳遞發生的,這就是爲何消息如何在actor之間傳遞的精確語義值得講的章節。安全

爲了給下面的討論提供一些上下文,請考慮跨越多個網絡主機的應用程序。不管是發送到本地JVM上的actor仍是遠程actor,通訊的基本機制都是相同的,可是在傳遞延遲方面會有明顯的差別(可能還取決於網絡鏈路的帶寬和消息大小)和可靠性。若是發送遠程消息,則涉及更多步驟,這意味着更多可能出錯。另外一個方面是本地發送將傳遞對同一JVM內的消息的引用,而不對發送的基礎對象進行任何限制,而遠程傳輸將對消息大小設置限制。性能優化

編寫你的Actor使得每次互動均可能是遙遠的是安全的。這意味着只依賴於那些始終保證的屬性,這些屬性將在下面詳細討論。這在actor的實現中有一些開銷。若是您願意犧牲完整的位置透明度 - 例如在一組密切合做的Actors的狀況下 - 您能夠將它們始終放在同一個JVM上,並在郵件傳遞上享受更嚴格的保證。下面將進一步討論這種權衡的細節。網絡

做爲補充部分,咱們提供了一些指導,說明如何在內置的可靠性之上構建更強的可靠性。本章最後討論了「Dead Letter Office」的做用。分佈式

通常規則

這些是消息發送的規則(即tell或!方法,它也是ask模式的基礎):工具

  • at-most-once delivery
  • message ordering per sender–receiver pair

第一個規則一般也能夠在其餘actor實現中找到,而第二個規則特定於Akka。性能

討論:「at-most-once」是什麼意思?

在描述傳遞機制的語義時,有三個基本類別:測試

  • at-most-once:傳遞意味着對於傳遞給機制的每條消息,該消息一次傳遞或根本不傳遞;從更隨意的角度來看,這意味着消息可能會丟失。優化

  • at-least-once:傳遞意味着對於傳遞給該機制的每一個消息,可能在傳遞它時進行屢次嘗試,使得至少一個成功;再次,從更隨意的角度來看,這意味着消息可能會重複但不會丟失。spa

  • exactly-once:交付意味着對於遞交給機制的每一個消息,只有一個交付給接收者;消息既不會丟失也不會重複。

第一個是最便宜的最高性能,最少的實現開銷 - 由於它能夠以一種即發即忘的方式完成,而不會在發送端或傳輸機制中保持狀態。第二個須要重試以對抗傳輸損耗,這意味着將狀態保持在發送端而且在接收端具備確認機制。第三個是最昂貴的 - 而且所以具備最差的性能 - 由於除了第二個以外它還須要將狀態保持在接收端以便過濾掉重複的交付。

討論:爲何沒有保證傳遞?

問題的核心在於這個保證究竟意味着什麼:

  1. 消息是在網絡上發出的?
  2. 該消息是由其餘主機收到的?
  3. 消息被放入目標actor的郵箱?
  4. 消息開始由目標actor處理?
  5. 目標actor是否成功處理了該消息?

其中每個都有不一樣的挑戰和成本,很明顯,在任何狀況下,任何消息傳遞庫都沒法遵照。

沿着一樣的路線,無人須要可靠消息的推理。發送者知道交互是否成功的惟一有意義的方式是接收業務級別的確認消息,這不是Akka能夠本身構成的。

Akka採用分佈式計算,並經過消息傳遞使通訊的可靠性顯而易見,所以它不會試圖欺騙和模擬漏洞抽象。這是一個在Erlang中取得巨大成功的模型,須要用戶圍繞它設計應用程序。您能夠在Erlang文檔(第10.9節和第10.10節)中閱讀有關此方法的更多信息,Akka會密切關注它。

在這個問題上的另外一個角度是,經過僅提供基本保證,那些不須要更高可靠性的用例不支付其實施成本;老是能夠在基本的基礎上增長更強的可靠性,可是爲了得到更高的性能,不可能主動地去除可靠性。

討論:消息排序

規則更具體地說,對於給定的一對Actor,直接從第一個發送到第二個的消息不會無序接收。這個詞直接強調,這種保證僅適用於與告訴操做員一塊兒發送到最終目的地,而不是在使用調解員或其餘信息傳播功能時(除非另有說明)。

他保證說明以下:

Actor A1 sends messages M1M2M3 to A2

Actor A3 sends messages M4M5M6 to A2

意味着:

1.若是M1已交付,則必須在M2和M3以前交付

2.若是M2已交付,則必須在M3以前交付

3.若是交付M4,則必須在M5和M6以前交付

4.若是M5已交付,則必須在M6以前交付

5.A2能夠看到來自A1的消息與來自A3的消息交織

6.因爲沒有保證傳送,所以能夠丟棄任何消息,即不到達A2

請務必注意,Akka的保證適用於郵件排入收件人郵箱的順序。若是郵箱實現不遵照FIFO順序(例如PriorityMailbox),那麼actor的處理順序可能會偏離排隊順序。

請注意,此規則不具備傳遞性:

Actor A sends message M1 to actor C

Actor A then sends message M2 to actor B

Actor B forwards message M2 to actor C

Actor C may receive M1 and M2 in any order

因果傳遞排序意味着在Actor C的M1以前從未接收過M2(儘管其中任何一個均可能丟失)。當A,B和C駐留在不一樣的網絡主機上時,因爲不一樣的消息傳遞延遲,可能會違反此排序,請參閱下文。

溝通失敗

請注意,上面討論的排序保證僅適用於Actor之間的用戶消息。actor的孩子的失敗是經過相對於普通用戶消息未被排序的特殊系統消息來傳達的。特別是:

Child actor C sends message M to its parent P

Child actor fails with failure F

Parent actor P might receive the two events either in order MF or F, M

緣由是內部系統消息具備本身的郵箱,所以用戶和系統消息的排隊調用的排序不能保證其出隊時間的排序。

In-JVM(本地)消息發送規則

建議不要依賴本節中更強的可靠性,由於它會將您的應用程序綁定到僅本地部署:應用程序可能必須以不一樣的方式設計(而不是僅僅採用某些參與者本地的一些消息交換模式)才能適合在一組機器上運行。咱們的信條是「設計一次,按照您但願的方式進行部署」,爲實現這一目標,您應該只依賴於「通常規則」。

Akka測試套件依賴於不丟失本地上下文中的消息(以及用於遠程部署的非錯誤條件測試),這意味着咱們確實盡最大努力保持測試穩定。可是,本地tell操做可能會失敗的緣由與JVM上的普通方法調用相同:

  • StackOverflowError
  • OutOfMemoryError
  • other VirtualMachineError

此外,本地發送可能會以特定於Akka的方式失敗:

  • 若是郵箱不接受該郵件(例如完整的BoundedMailbox)
  • 若是接收方在處理消息時失敗或已經終止

雖然第一個是配置問題,但第二個值得一些考慮:若是在處理時出現異常,則消息的發送者不會獲得反饋,而是通知發送給主管。這一般沒法與外部觀察者丟失的信息區分開來。

本地消息發送的排序

假設嚴格的FIFO郵箱,在某些條件下消除了上述消息排序保證的非傳遞性警告。正如您將注意到的,這些都是很是微妙的,將來的性能優化甚至可能使整個段落無效。

  • 在收到來自頂級actor的第一個回覆以前,有一個鎖保護內部臨時隊列,這個鎖是不公平的;這意味着能夠根據低級線程調度從新排序來自在演員構造期間到達的不一樣發送者的入隊請求(比喻地,細節更多涉及)。因爲JVM上不存在徹底公平的鎖,所以這是不可修復的。
  • 在構建路由器期間使用相同的機制,更確切地說是路由的ActorRef,所以使用路由器部署的actor存在一樣的問題。
  • 如上所述,問題發生在排隊期間涉及鎖定的任何地方,這也可能適用於自定義郵箱。

此列表已通過仔細編譯,但其餘有問題的狀況可能已經逃脫了咱們的分析。

更高層次的抽象

 基於Akka核心的小而一致的工具集,Akka還提供了強大的,更高級別的抽象。

如上所述,對可靠傳送要求的直接回答是明確的ACK-RETRY協議。這是最簡單的形式;

  • 一種識別單個消息以將消息與確認相關聯的方法
  • 重試機制,若是未及時確認,將從新發送消息
  • 接收器檢測和丟棄重複的方法

Dead Letters

沒法傳遞的消息(而且能夠肯定這些消息)將被傳遞給名爲/ deadLetters的合成actor。這種交付是在盡力而爲的基礎上進行的;它甚至可能在本地JVM內失敗(例如在Actor終止期間)。經過不可靠的網絡傳輸發送的消息將丟失而不會顯示爲dead letter。

此工具的主要用途是用於調試,特別是若是actor發送不一致(一般檢查死信會告訴您發送者或收件人在途中某處設置錯誤)。爲了有用於此目的,最好避免在可能的狀況下發送到deadLetters,即不時使用合適的死信記錄器運行應用程序(請參閱下面的詳細信息)並清理日誌輸出。這個練習就像全部其餘同樣 - 須要明智地應用常識:極可能避免發送給已終止的演員使發送者的代碼比調試輸出清晰度更復雜。

死信服務遵循與全部其餘消息發送相同的交付保證規則,所以不能用於實現有保證的交付。

我如何收到Dead Letter?

actor能夠在事件流上訂閱類akka.actor.DeadLetter,請參閱事件流以瞭解如何執行此操做。而後,訂閱的演員將從該點開始接收在(本地)系統中發佈的全部DeadLetter。DeadLetter不會經過網絡傳播,若是要在一個地方收集死信,則必須爲每一個網絡節點訂閱一個參與者並手動轉發它們。還要考慮在該節點生成DeadLetter,這能夠肯定發送操做失敗,遠程發送能夠是本地系統(若是不能創建網絡鏈接)或遠程發送(若是發送的是Actor)在那個時間點不存在)。

DeadLetter(一般)並不使人擔心

每當一個Actor按照本身的決定終止時,它發送給本身的某些消息就有可能丟失。有一個在複雜的關閉場景中很容易發生,一般是良性的:看到akka.dispatch.Terminate消息被丟棄意味着給出了兩個終止請求,但只有一個能夠成功。一樣地,你可能會看到akka.actor.Terminated來自子節點的消息,同時若是父節點在終止時仍在觀看子節點。

未完待續!

原文:https://doc.akka.io/docs/akka/2.5/general/message-delivery-reliability.html

相關文章
相關標籤/搜索