Akka手冊譯(一)——Actor引用,路徑與地址

本章描述了Actor如何被識別和定位在于一個分布式Actor系統。這與固有的主管層次一樣是Actor系統的核心內容,Actor之間的通信是透明的而它們可能位于多個網絡節點。

ActorRef.png

上圖顯示了Actor系統中最重要的實體之間的關系,請繼續閱讀相關細節。

什么是Actor引用

一個Actor的引用子型是ActorRef,它的最重要的目的是支持發送消息到它所代表的Actor。每一個Actor將訪問它的標準(本地)引用通過self字段。這個引用也包括發送方引用在默認情況下所有消息發送給其他Actor。相反,Actor在消息處理期間能通過發送方法訪問一個代表著發送者當前消息的引用。

根據Actor系統的配置衍生出幾種不同類別的Actor引用:

  • 純本地的Actor引用不支持網絡功能。這些引用不會作用于來自網絡訪問的遠程JVM。
  • 在遠程啟用時本地Actor在Actor系統中支持網絡功能,它的Actor需在同樣的JVM中。為了能重發到其它結點中,這些引用包括協議和遠程地址信息。
  • 有一種用于路由的本地Actor引用子類型(即Actor混合路由特點)。它的邏輯結構同上述的本地引用,但發送消息時用它們的子集代替直接分派。
  • 遠程Actor引用表示Actor可用遠程通訊,即透明的傳輸消息并發到遠程的JVM中。
  • 有幾種特殊類型的Actor引用,它們像本地Actor引用實現特定目的:
    • PromiseActorRef表示Promise已完成來自于Actor的響應。akka.pattern.ask 建立這個引用。
  • DeadLetterActorRef 默認實現死信服務,這種服務指Akka 路由的消息目標不存在或已停止。
  • EmptyLocalActorRef 指Akka查找一個不存在的本地Actor路徑。它等于DeadLetterActorRef ,但它保留了路徑以便Akka通過網絡發送且和其它存在的Actor引用比較,有一些在Actor結束前獲得。
  • 然而有一些看不到一次性的內部實現:
    • 有一種Actor引用并不表示一個Actor僅僅是做為根監督的偽監測。我們稱之謂吹泡泡時間。
  • 在第一個日志服務啟動前,事實上是Actor建立了一個仿真Actor引用接受日志事件并直接輸出屏幕,它是Logging.StandardOutLogger。

Actor路徑是什么

由于Actor嚴格按層次形式創建,就存在唯一的Actor名字的序列按監管鏈實現遞歸,從子到父直到根。 這個序列可以視為一個文件系統中封閉的文件夾,因此我們用“路徑”引述它。盡管Actor系統與文件系統的層次有根本性的區別。

Actor路徑由一個協議字,然后是路徑元素,從根到設計的Actor。路徑的名稱是由遍歷的Actor和斜杠組成。

Actor引用和路徑的區別是什么?

Actor引用是特定的單個Actor,引用的生命周期與Actor相同。Actor路徑表示一個名字,它可能對應也可能不對應Actor,且它沒有它的生命周期,一直存在。在建立Actor路徑時可以沒有Actor,但建立Actor引用時一定有對應的Actor。

創建一個Actor再終止它,再用同樣的路徑創建新的Actor。新建的Actor是新的化身,與原來的并是同一個。原來的引用對新的無效。消息發送給老的引用新的并不能收到哪怕是用了相同的路徑。

Actor路徑協議字

每一個Actor路徑有一個地址組件,描述協議和相應可達的Actor位置。在層次結構中從root起跟隨的Actor名字。比如

1."akka://my-sys/user/service-a/worker1"                   //本地
2."akka.tcp://my-sys@host.example.com:5678/user/service-b" // 遠程

這里akka.tcp是2.4版默認的傳輸方式,其它傳輸方式可用插件化實現。主機和端口部分的說明(即host.example.com:5678)取決于所使用的傳輸機制,但它必須遵守URI結構規則。

邏輯Actor路徑

從父級主管到根的唯一路徑稱為邏輯路徑。它是由上代Actor創建并精確匹配,所以一旦遠程設置里配置(含地址和路徑)它能確定Actor。

物理Actor路徑

基于遠程布署意味著Actor可以在不同的網絡主機和不同的父級被創建,例如在不同的Actor系統。而邏輯路徑只在一個系統中描述了功能位置。在這種情況下,按路徑從根守護遍歷網絡是巨大的開銷。因此每個Actor有一個物理路徑,從根守護開始駐留實際的對象。查詢其他Actor時作為發送者引用使用這個路徑將使它們直接向這個Actor返回,使路由延時最小化。

要注意的是物理路徑不跨越多個Actor系統或JVM。這就使Actor(層次主管)的邏輯路徑和(Actor發布)的物理路徑在上級是無遠程主管的可能有分岔。

Actor 路徑是別名或符號嗎

在一些真實的文件系統中可能認為Actor的路徑別名或符號連接,比如Actor能用一個以上的路徑找到。然而Actor層次不同于文件系統層次。并不能自由創建象符號鏈接那樣關聯到任意Actor。如上節邏輯路徑和物理路徑描述的那樣,Actor路徑要么是體現監管層次的邏輯路徑,要么是代表Actor發布的物理路徑。

Actor引用如果獲得

獲得Actor引用有兩類方法:建立Actor和查找Actor,后者更有利于從具體的路徑建立引用并查詢到邏輯層次。

創建Actor

Actor系統通常開始于在守衛Actor下用ActorSystem.actorOf方法創建Actor隨后用ActorContext.actorOf方法創建Actor衍生Actor樹。這些方法返回新建Actor的引用。每個Actor將通過引用直接訪問它的父級,自身與子級。這些引用將發送消息到其它Actor,并使他們直接回復。

通過具體路徑查詢Actor

另外,Actor引用可以用ActorSystem.actorSelection方法查詢到。在接收到的每條消息中,選擇到的內容能對通信間的Actor匹配相對應的內容。
發送一個消息到綁定到特定生命周期Actor上用ActorRef實現,就象內置的“標識”消息Actor用sender()回復引用。

絕對路徑與相對路徑

除了ActorSystem.actorSelectionActorContext.actorSelection,還有用于所有Actor的context.actorSelection。這就像它的兄弟ActorSystem,但它是從Actor樹的根開始到當前Actor止的查詢。路徑元素由兩個點組成("..")用于訪問父級Actor??梢韵窭幽菢影l送到相鄰Actor中:

 1. context.actorSelection("../brother") ! msg

絕對路徑也有常用的查詢方法,例如

 1. context.actorSelection("/user/serviceA") ! msg

查詢Actor邏輯層次

由于Actor系統的形式像文件系統層次,所以路徑的形式就像Unix Shell的方式:可以用通配符《*》和《?》代替(部分)元素名字,結果將得到0個或多個Actor。因為結果不是單個Actor引用,與ActorSelection類型不同,且不支持ActorRef的操作集。用ActorSystem.actorSelectionActorContext.actorSelection 方法支持發送的內容:

 1. context.actorSelection("../*") ! msg

將發送消息到所有鄰近結點包括自已。用actorSelection得到引用為完成消息發送將遍歷主管層次。當消息正在到達接收者時選擇到匹配的Actor集可能改變事件,因此無法觀察到消息的實時改變。為了提取發送者引用,通過發送請求并收集所有應答者解決不確定性,然后觀察所有的發現的具體的Actor。這個解決方案可能在未來版本中改進。

摘要:actorOfactorSelection

注意
上面的部分中描述的一些細節可以概括和容易記住如下:

  • actorOf僅僅是創建一個新Actor,且創建它直接子集調用的上下文(它可能是任意的Actor或Actor系統)
  • actorSelection僅僅是消息收到時查找已存在Actor,即不創建Actor,或在建立選擇時校驗存在的Actor。

Actor引用等式與路徑等式

ActorRef等式目的是使ActorRef能對應到目標的化身。兩個引用在有同樣的路徑和指向同樣的Actor化身視為相等。一個引用指向已終止的Actor無法與另一個用同樣路徑(重建)的Actor比較。注意,因錯誤導致重啟的Actor將有同樣的Actor化身即重啟對ActorRef的消費者不可見。

如果需要在集合里保持跟蹤Actor引用且不關心那些用ActorPath做關鍵字的精確的Actor化身,因為目標Actor的標識符在路徑比較時不會包含在內。

重用Actor路徑

當Actor終止時,其引用將指向死信郵箱,DeathWatch會實現最后的傳輸且一般不會復活(因為Actor生命周期不允許這么做)。可能在稍后的時間內創建一個完全相同路徑的Actor,原因是無法執行相反的僅所有Actor保持有效創建。這不是個好做法:消息以actorSelection發送給Actor,而它已經‘死亡’又突然再次工作,在傳輸和其它事件期間沒有任何可靠的順序,因此新的路徑占用者可能會收到以前占用者的消息。

在非常特殊的情況下是正確的,而確信能精確的控制的是Actor的主管。因為這是唯一的在創建新子級失敗前可測到名字被取消。

也有可能在測試過程中需要,當測試對象依賴于特別路徑。這種情況下最好模擬主管,以便于終止消息產生自適當的點,使后者在適當的時候取消名字。

遠程布署的相互作用

當Actor創建一個子級時,Actor系統布署者會決定新的Actor會駐留在同樣的JMV中或新的節點中,第二種情形是創建Actor會觸發不同JVM、跨越Actor系統的連接。遠程系統會放置新的Actor路徑而保留它的目的。新Actor的主管會成為遠程Actor引用(表示Actor會觸發它的創建)。在這種情況下,context.parent(主管引用)和context.path.parent(Actor路徑上的父結點)表示不同Actor。然而主管查找子級的名字時會發現它在遠程結點,這樣能保護邏輯結構向無法解析的引用發送時。

RemoteDeployment.png

地址的用途是什么

當通過網絡發送一個Actor引用時,它由路徑表示。因此,路徑必須對所有必要的信息有完整編碼在向低層Actor發送消息時。這是由協議編碼,主機和端口及地址部分的路徑字符串。當Actor系統收到從遠程節點Actor路徑時,將檢查路徑地址是否是匹配本系統。此時會被解析為本地引用,否則會表示為遠程引用。

頂級范圍的Actor路徑

在路徑層次的根部,根守護在所有的Actor之上,它的名字是“/",下一級包括如下內容:

  • "/user" 是為所有用戶創建的高級Actor的守護,Actor被ActorSystem.actorOf創建且在這個節點下。
  • "/system"是為所有系統創建的高級Actor的守護,例如日志監聽或Actor啟動時按配置自動布署。
  • "/deadLetters" 是死信Actor,在所有消息發送時停止或不存在時重新路由的目標(盡力而為,消息甚至在本地JVM時丟失)
  • "/temp"是為所有系統創建的短期高級Actor的守護,例為用于實現ActorRef.ask
  • "/remote" 是一個人造路徑在所有Actor之下,它將駐留遠程引用。
    Actor的名稱空間需要這樣的結構從中央出現且設計目標是:層次中都是Actor,所有Actor有同樣的方法。因此,不僅可以查找自已創建的,也可以查找系統守護并向它發送消息(這種情況下會被放棄)。這非常有用,它使整個系統更一致。

如果想讀到更多Actor系統的高級結構,看《頂級主管》一節。

上一篇
下一篇

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容