Akka手冊譯(一)——消息傳遞的可靠性

Akka幫助您構(gòu)建可靠的應(yīng)用程序在一臺機器上使用多個處理器核心(“擴大”)或分布在計算機網(wǎng)絡(luò)(“擴張”)。關(guān)鍵的抽象使所有交互者在代碼單元——Actor——發(fā)生在消息傳遞過程中,這就是為什么在參與者之間傳遞消息時以精確的詞義得到它們的內(nèi)容。

以下的討論涉及到考慮到應(yīng)用拆分到多個網(wǎng)絡(luò)主機上。不論發(fā)送到在本地JVM的Actor或遠(yuǎn)程的Actor基本的通信機制是一致的,當(dāng)然可以觀察到交付信息的延遲不同(可能取決于網(wǎng)絡(luò)鏈路的帶寬和消息大小))和可靠性。對于遠(yuǎn)程消息發(fā)送顯然有更多的步驟,這意味著更多的出錯可能性。另一個方面是,本地傳遞只會在同一個JVM中通過引用信息,沒有任何限制底層對象發(fā)送,而遠(yuǎn)程運輸將受消息大小限制。

編寫Actor時采用悲觀策略以使每個交互者遠(yuǎn)程是安全的。這只有依靠屬性來保證,并在下面詳細(xì)討論這些內(nèi)容。有一些開銷發(fā)生在Actor實現(xiàn)時。如果樂意放棄的透明傳輸比如用一組緊密的Actor做樣例,把它們在同一個JVM只使消息傳遞完全可靠。下面將進(jìn)一步權(quán)衡這方面細(xì)節(jié)。

作為一種補充部分,我們將涉及到一些頂級內(nèi)置部分建立更高可靠性。這章結(jié)尾將討論一下“死信機制”。

一般的規(guī)則

這些是消息發(fā)送的規(guī)則(即tell或!方法,也構(gòu)成詢問模式):

  • 至多一次交付,例如非擔(dān)保的交付;
  • 發(fā)送者-接收者每個消息有序化成對;
    第一條規(guī)則通常發(fā)現(xiàn)在其它Actor實現(xiàn),當(dāng)?shù)诙lAkka特定于它們。

討論:至多一次交付是含義

描述交付機制的語義有三類:

  • 至多一次交付指每個消息的機制是1次或0次交付,更直白的說消息可能會丟失。
  • 至少一次交付指每個消息的機制是嘗試多次交付,并有一次成功。更直白的說消息會重復(fù)但不會丟失。
  • 只一次交付指每個消息只會交付給接收者一次,消息可能即不會被復(fù)制也不重復(fù)。
    第一個代價最低——高性能,低開銷——因為它實現(xiàn)即發(fā)即棄的方式不在發(fā)送端或傳輸狀態(tài)上保持狀態(tài)。第二個需要累計傳輸失敗量,這意味著發(fā)送端保持狀態(tài)以及接收端實現(xiàn)確認(rèn)機制。第三個代價最高,之所以性能最差,因為要保持接收端的狀態(tài)還要過慮掉重復(fù)數(shù)據(jù)。

討論:為什么用非擔(dān)保交付

以上這個問題的核心是擔(dān)保表示:

  1. 網(wǎng)絡(luò)上的消息發(fā)送出去嗎?
    2.消息在其他主機接收嗎?
  2. 消息放到目標(biāo)Actor的郵箱了嗎?
  3. 目標(biāo)Actor開始處理消息了嗎?
  4. 目標(biāo)Actor的消息處理成功了嗎?
    這些的每一個都有各自的挑戰(zhàn)和開銷,很明顯在消息處理庫中有條件的將無法完成。考慮例子的配置郵箱類型以及一個郵箱的邊界如何與第三點相互,甚至決定以上五個點的“成功”是什么。

沿這幾點的推理得到“Nobody Needs Reliable Messaging.”發(fā)送方知道的接收業(yè)務(wù)級別的確認(rèn)消息成功的唯一意義在于,Akka不是可以自己組成的。(這不是一個想怎么做就能怎么做的框架)。

Akka依賴分布式并使用不可靠的通信傳遞消息,因此它不保存可以想像成漏水一樣。這個模型已經(jīng)在Erlang中的取得了巨大的成功,用戶圍繞它來設(shè)計他們的應(yīng)用程序??梢蚤喿x更多關(guān)于這種方法在Erlang文檔(10.9和10.10節(jié)),Akka密切關(guān)注它。

另一個角度看這個問題,它只提供基本保障的用例不需要更強的可靠性不支付的成本實施;它總是可以添加更強的可靠性的基本內(nèi)容上,但這是不可能的倒行逆施的刪除為了獲得更多的性能可靠性。

討論:消息排序

更具體的說,對于給定的兩個Actor,消息收到時不會無序。這強調(diào)了只能保證應(yīng)用在發(fā)送時明確發(fā)送源和目標(biāo)時,而不是使用介質(zhì)或其他信息傳遞特性(除非另有說明)。
如下例這樣保障:

Actor A1發(fā)送消息 M1,M2,M3 到 A2
Actor A3發(fā)送消息 M4,M5,M6 到A2
這意味著:

  1. M1必須在M2和M3之前交付;
  2. M2必須在M3之前交付;
  3. M4必須在M5和M6之前交付;
  4. M5必須在M6之前交付;
  5. A2能從A1和A3交叉看到消息;
  6. 由于不是保證交付,任何消息都可能拋下,即沒有到達(dá)A2

注意
Akka保證適用于消息隊列的順序進(jìn)入收件人的郵箱。如果郵箱遵循FIFO實現(xiàn)順序(例如PriorityMailbox),然后處理順序的Actor將偏離入隊秩序。

注意這些規(guī)則是不可達(dá)的

Actor A發(fā)送消息M1 到Actor C
Actor A 然后發(fā)送消息M2 到Actor B
Actor B轉(zhuǎn)遞消息M2到Actor C
Actor C可能以任何順序收到M1和M2

由于可達(dá)順序是M2在M1在Acotr C收到后再收到(盡管其中任何一個可能丟失)。這個順充可能不確定因不同消息延遲,在A,B,C在不同的網(wǎng)絡(luò)主機上。請參閱下文。

注意
Actor創(chuàng)建被視為一個消息從父級發(fā)送到子級,如同上面所討論的。在發(fā)送消息到Actor時能重新在初始化時重新排序就意味著消息沒有到達(dá)因為Actor還沒生成。舉個例子發(fā)送一個消息從R2引用發(fā)送過來時,消息可能太早到而不能創(chuàng)建遠(yuǎn)程布署的Actor R1時。更好的定義順序是創(chuàng)建Actor后立即發(fā)送一個消息給它。

通訊失敗

請注意以上兩個Actor間保證順序的討論僅限于用戶消息。Actor的子級通信是特別的系統(tǒng)消息,與用戶消息的順序無關(guān)。特別是:

子Actor C發(fā)送消息M到它的父級P
子Actor F處理失敗
父Actor P可能收到兩個事件順序M,F(xiàn)或F,M

原因是內(nèi)部系統(tǒng)消息有自己的郵箱調(diào)用用戶排隊的順序和系統(tǒng)的消息不能保證出列的訂購時間。

JVM內(nèi)(本地)消息發(fā)送規(guī)則

留意正下這節(jié)所要做的

在這一節(jié)中依賴較強的可適應(yīng)性是不可取的,從應(yīng)用程序綁定到本地布署上。應(yīng)用程序被設(shè)計的不同(不僅僅是一些消息交換模式和一些Actor)以適應(yīng)一個集群上運行的機器。我們的信條是一次設(shè)計,任意布署。而要實現(xiàn)這一點,人應(yīng)該只依賴于一般規(guī)則。

本地發(fā)送的可靠性

Akka測試套件的依賴在本地的上下文中沒有丟失的消息(及遠(yuǎn)程開發(fā)沒有錯誤測試),這意味著我們努力保持測試穩(wěn)定。本地tell操作可能因為一些原因的錯誤就如同通常在JVM中調(diào)用的方法。

  • StackOverflowError
  • OutOfMemoryError
  • other VirtualMachineError
    此外,本地發(fā)送有特定的Akka方法使發(fā)送時出錯:
  • 比如郵箱沒有接收到消息(如,BoundedMaibox滿了)
  • 接收的actor處理失敗或已經(jīng)終止
    當(dāng)排除每一個錯誤的配置引起第二個時,第二個消息得不到反饋在處理異常。通知會被它的主客代替。這是一般不區(qū)分外部觀察者的失去了消息

本地消息發(fā)送順序

上述警告的不及物的消息假定在嚴(yán)格的FIFO郵箱中在特定條件下被消除。會注意到,這些很細(xì)微甚至涉及到未來優(yōu)化整個段落。這可能是下列不完整的幾點:

  • 在收到頂級的Actor回復(fù)前,有一個內(nèi)部鎖保護內(nèi)部監(jiān)時隊列,這把鎖不是直接的。這意味著排隊請求期間從不同的發(fā)送者到acotr的結(jié)構(gòu)(比如,細(xì)節(jié)更復(fù)雜)可能被重新排序根據(jù)底層線程調(diào)度。由于不存在完全公平鎖在JVM上,這是認(rèn)識上的誤區(qū)。
  • 使用相同的機制在建設(shè)一個路由器,更精確地ActorRef路由,因此Actor與路由器部署存在同樣的問題。
  • 如前所述,發(fā)生任何鎖的問題是涉及在入隊,這可能也適用于自定義郵箱。
    這個列表已經(jīng)仔細(xì)編制,但其他問題場景可能逃脫了我們的分析。

本地排除和網(wǎng)絡(luò)排序如何實施

對于一個給定的規(guī)則對Actor、消息發(fā)送直接從第一個到第二個不會收到無序適用于通過網(wǎng)絡(luò)發(fā)送的消息與基于TCP的Akka遠(yuǎn)程傳輸協(xié)議。
在前一節(jié)中解釋當(dāng)?shù)叵l(fā)送服從傳遞因果順序在特定條件下。這個命令可以違反了由于不同的消息傳遞延遲。例如:

node-1上Actor A發(fā)送消息M1到node-3上的actor C
然后node-1的Actor A上發(fā)送消息M2到node-2的Actor B
node-2上ActorB轉(zhuǎn)發(fā)消息M2到node-3的 Actor C
Actor C 可能以任何順序收到M1 和 M2

M1可能花很長時間旅行到node-3 比M2旅行經(jīng)過node-3經(jīng)過node-2

高級抽象

Akka提供強大的、更高層次的抽象基于一個微小而持續(xù)的Akka核心工具集。

消息傳遞模式

正如上面所討論的可靠傳遞的要求是一個顯式的ACK-RETRY協(xié)議。在其最簡單的形式要求

  • 一種識別個人信息和確認(rèn)相關(guān)信息
  • 重試機制,如果不確認(rèn)將重新發(fā)送消息
  • 為接收器檢測和丟棄重復(fù)
    第三種成為必要借助確認(rèn)不必要到達(dá)。ACK-RETRY協(xié)議與業(yè)務(wù)級別的確認(rèn)支持“至少一次”的Akka持久模塊交付。副本可以檢測到跟蹤消息的標(biāo)識符通過“至少一次”交付。另一種實現(xiàn)第三部分將使處理消息冪等層面的業(yè)務(wù)邏輯。

實現(xiàn)這三個需求的另一個例子是在可靠的代理模式(這是現(xiàn)在取代“至少一次”交付)。

事件源

事件源(和共享)用于制造大型網(wǎng)站規(guī)模數(shù)十億的用戶,這個想法非常簡單:當(dāng)一個組件(Actor)過程命令將生成一個事件列表代表命令的效果。這些事件存儲除了應(yīng)用于組件的狀態(tài)。這個方案的優(yōu)點是,事件只添加到存儲,沒有什么是永遠(yuǎn)的突變;這使得完美的復(fù)制和擴展消費者的事件流(即其他組件可以使用事件流來復(fù)制組件的狀態(tài)在一個不同的容器或反應(yīng)的變化)。如果組件的狀態(tài)因為機器故障或被排擠出緩存它可以很容易地重建重演了事件流(通常采用快照來加快這一進(jìn)程)。事件源支持Akka持久性。

郵箱的明確確認(rèn)

通過實現(xiàn)一個自定義郵箱類型可以重試的消息處理接收Actor的一端為處理臨時失敗。此模式主要是有用的本地通信上下文交付擔(dān)保否則足以滿足應(yīng)用程序的需求。

請注意,規(guī)則的警告在jvm(本地)消息發(fā)送申請。

實現(xiàn)這種模式的一個例子是顯示在郵箱與明確的確認(rèn)。

死信

消息不能交付(這可以確定)將交付給一個叫做 /deadLetters合成的Actor。這交付發(fā)生在力所能及;它可能會失敗甚至在本地JVM(例如在Actor終止)。通過發(fā)送的消息不可靠的網(wǎng)絡(luò)傳輸將丟失沒有出現(xiàn)死亡的信件。

死信用于哪些方面

這個設(shè)備的主要用途是為調(diào)試,特別是如果一個Actor發(fā)送與到達(dá)不一致(通常檢查死者字母會告訴你,發(fā)送方或接收方設(shè)置錯了沿途某處)。為了有效使用它盡可能避免發(fā)送deadLetters,即運行您的應(yīng)用程序與一個合適的死信記錄器(參見下面的更多)不時和清理日志輸出。這種需要明智的應(yīng)用常識:很可能是避免發(fā)送Actor終止,使發(fā)送方的代碼更清晰。

死信服務(wù)遵循相同的規(guī)則對交付擔(dān)保和其他消息發(fā)送,因此它不能用于實現(xiàn)保證交付。

如何接收死信

Actor可以訂閱akka.actor.DeadLetter在事件流,Event Stream顯示如何使用它。訂閱的Actor將會收到所有死信件發(fā)表在這一點的本地系統(tǒng)。死信不是通過網(wǎng)絡(luò)傳播,如果想收集在一個地方要訂閱一個Actor/手動網(wǎng)絡(luò)節(jié)點和轉(zhuǎn)發(fā)。死信在這個節(jié)點生成可以確定發(fā)送操作失敗,可以本地系統(tǒng)為遠(yuǎn)程發(fā)送(如果沒有可以建立網(wǎng)絡(luò)連接)或遠(yuǎn)程(如果你發(fā)送的Actor不存在在這個時間點)。

死信不需要擔(dān)心

每一次Actor不會由自已決定停止,因為有一可能它自已發(fā)送丟失。一些復(fù)雜的自動關(guān)閉場景是良性的:看到akka.dispatch.Terminate。終止消息意味著兩個終止請求,當(dāng)然只有一個能成功。同樣,你可能會看到akka.actor.Terminated。但父級看孩子時終止消息Actor的層次結(jié)構(gòu)在死信從孩子開始。

上一篇
下一篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本章描述了Actor如何被識別和定位在于一個分布式Actor系統(tǒng)。這與固有的主管層次一樣是Actor系統(tǒng)的核心內(nèi)容...
    兒哥欠三百首閱讀 2,018評論 0 0
  • Actor系統(tǒng)的實體 在Actor系統(tǒng)中,actor之間具有樹形的監(jiān)管結(jié)構(gòu),并且actor可以跨多個網(wǎng)絡(luò)節(jié)點進(jìn)行透...
    JasonDing閱讀 3,532評論 2 6
  • 本章概述了監(jiān)督的原語語義和背后的概念。而如何轉(zhuǎn)化為實際代碼,請參閱相應(yīng)的章節(jié)Scala和Java api。 主管的...
    兒哥欠三百首閱讀 1,097評論 0 0
  • 前一節(jié)解釋了Actor系統(tǒng)中Actor的層級以及構(gòu)建應(yīng)用程序的最小單元。這一節(jié)則看單個Actor,解釋它的概念并去...
    兒哥欠三百首閱讀 1,495評論 0 0
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,715評論 19 139

友情鏈接更多精彩內(nèi)容