akka學習之actor監護與監控

2021-08-27 19:51:57 字數 4450 閱讀 3654

在前面的章節akka學習之actor介紹中介紹了actor對ziactor有監護職責,同時actor對其他actor的生命週期等資訊進行監控。這張將詳細解釋什麼是監護和監控。

在actor系統,乙個actor(上司也可以成為監護人)可以建立子actor(下屬),然後可以分配任務給他的下屬去執行,同時也要對下屬的錯誤負責,上司也是下屬的監護者。當下屬發生了錯誤,比如丟擲了乙個異常,下屬會終止自己以及它的下屬,然後傳送乙個訊息給他的上司(監護人),表示發生了錯誤。根據具體的任務內容以及錯誤的型別,監護人可能會採取下面某種措施:

1.儲存下屬當前的狀態,並讓下屬重試一次。

2.清除下屬的當前狀態,重啟下屬

3.永久的終止下屬

4.把錯誤交給他的監護者來處理,並讓自己處於錯誤狀態。

要理解akka監護的思想,必須牢記一點:把每乙個actor視為整個akka監護層級體系的一部分,因為乙個actor可能既是監護者,又是被監護者。終止乙個actor,也同時終止了這個actor的所有下屬,同樣重啟乙個乙個actor也同時重啟了這個actor的所有下屬。actor類的重啟鉤子prerestart方法的預設行為是在重啟前終止它的所有下屬,不過你也可以override這個方法。

每乙個監護者都必須實現乙個錯誤處理方法,這個方法使用上面4個措施中的一種來處理所有可能的錯誤,必須注意的是,不要把發生錯誤的actor物件作為這個方法的輸入。但是你可能發現這種錯誤處理的框架可能不夠靈活,比如你想要實現針對不同的下屬使用不同的處理策略。由於akka的監管方式是可以遞迴式得公升級,在某個actor上做太多種的監控策略可能會導致你很難排查問題。比如你在乙個actor中捕獲並處理了n中不同的exception型別,那麼你排查問題的時候就可能不知道這個異常是它哪個層級的下屬丟擲的。因此最好在系統中加入乙個專門的監護層。

akka的監護方式是一種「父監護」形式,就像父母是兒女的監護人一樣。乙個actor的建立只能通過其他的actor來建立(最頂層的actor是akka庫提供的),建立者就是父actor,被建立者是子actor,乙個actor的監護人就是建立它的actor。這種自上而下的樹形結構使得akka的監護層級結構更加清晰,同時也保證了乙個actor不會成為孤兒,或者擁有父actor之外的監護人。另外,這也使得關閉應用程式的步驟是自下而上逐層關閉,步驟更加清晰。

父子actor之間監護關係是是通過特殊的系統訊息來連線的,並且這些特殊的訊息有自己單獨的系統郵箱。因此這些特殊的訊息與普通的使用者訊息之間沒有明確的順序,使用者也沒有辦法來干預特殊訊息和使用者訊息之間的順序。

乙個actor系統建立之後,他至少包含了3個actor:根守護actor,系統守護actor,使用者守護actor。如下圖所示:

所有使用者actor的根actor都是使用者守護actor,是使用者建立的乙個層級的actor的父actor和監護者,它的名字就是「/user」,所有通過system.actorof()方法建立的actor都是他的子actor。當使用者守護actor停止了,所有的使用者建立的actor也將會停止,同時它的監護策略也決定了使用者建立的第一層actor的錯誤是如何被處理的。在akka2.1之後的版本中可以通過akka.actor.guardian-supervisor-strategy(它使用supervisorstrategyconfigurator的完整類名)來配置它的監護策略。當使用者守護actor向它的監護者根守護actor提交了乙個錯誤,根守護將會停止使用者守護actor,進而關閉整個actor系統。

在應用中,我們常常需要使用日誌來記錄整個系統模組的關閉順序和行為,因此日誌模組會在所有的actor停止之前還在工作,即使日誌模組本身也是乙個actor。為了實現這個功能,需要乙個系統守護者來保證日誌模組在最後關閉,已經管理整個系統在接收到停止訊息之後的關閉行為。系統守護actor的監護策略是,在接收到除 actorinitializationexception 和actorkilledexception的所有異常時會提交到根守護者然後重啟整個系統,actorinitializationexception 和actorkilledexception會停止發生錯誤的子actor。

根守護actor是所有所謂的頂層actor的祖父,它監護著所有的特殊actor,它的監護策略是supervisorstrategy.stoppingstrategy,它不管收到什麼異常,都關閉出問題的子actor。前面說過每乙個actor都有自己的監護者,但是根守護actor是整個系統的起點,那麼它的監護者是誰呢?其實根守護

actor是乙個虛擬的actor引用,它會停止第乙個向它匯報錯誤的子actor,然後在整個actor系統都停止之後把actor系統的isterminated狀態設定為true。

導致actor處理某個訊息時發生錯誤的情況,大致可以分為3類:

1.處理某個訊息時發生系統錯誤,比如資料不合法,導致空指標異常,型別轉換錯誤等等。

2.依賴的外部資源發生錯誤

3.actor內部狀態崩了

第3種錯誤很難區分具體的情況,除非能夠明確知道哪種錯誤,否則需要直接reset內部狀態。如果監護者能確定某個子actor的錯誤不會對監護者本身以及其他子actor造成影響,那麼最好重啟這個錯誤actor。重啟actor實際上就是重新建立乙個這個actor的例項,並且把個actor的actor引用指向新的例項。這也是前面actor介紹中講述的為什麼actor要封裝在actor引用裡面的原因。然後新的actor例項重新開始出來郵箱中的訊息,不過導致就的actor發生錯誤的那條訊息不會再重新處理。所以actor的重啟對外界來說是透明的。actor重啟的精確的流程如下:

1.停止這個actor,不再處理任何訊息,並且遞迴地停止它所有的子actor。

2.呼叫舊這個actorprerestart鉤子,預設行為是傳送終止請求給所有的子actor,並且呼叫poststop鉤子執行停止之後的行為。

3.等待所有接收到終止訊息的子actor完全終止,當然這個等待是非阻塞的,只有最後乙個終止的子actor傳送終止訊息訊息過來之後,才開始執行下一步。

4.通過原來提供的actor工廠建立乙個新的actor例項。

5.在新的例項中呼叫postrestart鉤子(預設行為是呼叫prestart鉤子)。

6.傳送重啟請求給所有在第3步中被殺死的子actor,遞迴地從第2步開始重啟所有子actor。

7.重新開始執行actor的訊息處理行為。

上面講的監護關係是一種相當於父母對子女的監護關係,即只有建立它的父actor才能監護它。而監控(monitor)則不侷限於血緣關係,只要乙個actor能拿到另乙個actor的引用,就可以監控它。由於actor活著建立出來以及重啟對於監護者之外的actor是不可見的,所以監控者能到監控到的狀態變化只能是從生到死的變化。相比於監護者是對被監護者的錯誤做出反應,而監控者是對被監控者的終止做出處理。

生命週期的監控是通過監控者接收被監控者傳送的終止訊息來實現的。預設的是實現是丟擲乙個

deathpactexception異常。開始終止訊息的監聽是通過

actorcontext.watch(targetactorref)來實現,而停止終止訊息的監聽則是呼叫

actorcontext.unwatch(targetactorref)。需要注意的是,終止訊息的傳遞與被監控者終止行為的發生是沒有順序的,所以及時事實上被監控actor已經終止,監控者還是需要繼續接收終止訊息。

當監護者不能通過簡單的重啟來恢復子actor而不得不終止它,那麼監控就顯得尤為重要。

比如actor初始化錯誤,這種情況下就必須監控這些子actor,以便重新建立或者定期重試actor。另外乙個監控比較有用和常見的場景是actor可能遇到所依賴的外部資源的缺失。當然也可能是它自己的子actor的缺失,比如第三方通過system.stop(child)方法或者致命毒丸終止了它的子actor,父actor也會受到影響。

一對一策略與全部對一策略在阿akka中,監護策略大致可以分為兩類:一對一策略與全部對一策略。這兩類策略的使用都是通過配置監護者策略與對應的異常型別匹配,以及子actor在停止前允許發生的錯誤次數來實現。一對一策略是只對發生錯誤的子actor進行處理,而全部對一策略則是對發生錯誤的actor的兄弟姐妹也進行處理。

一般情況下使用一對一策略就可以了,同時akka的預設方式也是一對一。全部對一策略的適用場景是actor的組裝互相依賴於它的兄弟姐妹,這種場景下乙個actor的錯誤會導致它的兄弟姐妹也發生錯誤。因為重啟不會情況郵箱中的訊息,所以最好是終止並且重建發生錯誤的actor,否則你要確保重啟之後郵箱中的訊息不會導致actor繼續發生錯誤,不然你的actor將陷入無休止的重啟中。

在全部對一策略中一般終止乙個子actor並不會自動的終止其他子actor。但是可以簡單地通過監控生命週期來實現:如果監護者沒有處理終止訊息,監護者將丟擲乙個deathpactexception

並且重啟自己,它預設的

prerestart

方法將停止所有的子actor。當然也可以明確定義處理方法來實現。

必須注意到,如果在使用全部對一策略的監護者中建立一次性的actor,那麼這個臨時性的actor發生錯誤會影響那麼永久性的actor。如果不想這種情況發生,那麼應該建立乙個中間監護者:這很容易通過為工作者宣告乙個size為1的路由實現。

Akka學習筆記(3) Actor

actor是akka中的核心概念,它為併發和分布式提供了一種更高階別的抽象,使併發程式設計更加容易。定義actor 定義乙個actor非常簡單 繼承actor,並提供receive方法即可。不帶構造引數的actor class myactor1 extends actor 帶構造引數的actor c...

Akka學習筆記 Actor訊息傳遞 2

文章目錄 hide 3 teacher actor 我們在前面僅僅討論了actorref的quoterequest,並沒有看到message的類!這裡將介紹,如下 1packageme.rerun.akkanotes.messaging.protocols 2 3objectteacherproto...

Akka 2 5 12學習系列(Actor模型)

carl hewitt在1973年提出了actor模型。actor模型是乙個併發程式設計的數學模型,把 actors 作為併發計算的通用原語。當接收到乙個訊息後,actor可以做出以下反應 做出本地決策,創造更多actor,傳送更多訊息,並且決定如何對下一條訊息做出反應。actor可以改變自己的狀態...