Akka系列---什么是Actor
本文已.Net語法為主,同時寫有Scala及Java實(shí)現(xiàn)代碼
?
嚴(yán)肅的說,演員是一個廣泛的概念,作為外行人我對Actor 模型的定義:
Actor是一個系統(tǒng)中參與者的虛擬人物,Actor與Actor之間是可以相互溝通,所有的溝通都是通過Message
比如說一個呼叫中心,數(shù)以百萬計(jì)的客戶可能會呼叫一個1-800的號碼,并與許多可能的客戶服務(wù)代表進(jìn)行對話
向這樣類型的交互可以使用演員建模
?
在Actor模型中,一切都是Actor,就想在面向?qū)ο缶幊?OOP)中一切都是"Object"一樣.在OOP中,你需要使用類和對象進(jìn)行建模,在Akka中,你可以使用Actor和Message進(jìn)行建模
以下是Akka中基本例子
using System;
using Akka.Actor;
namespace ActorsSendingMessages
{
? ? /// <summary>
? ? /// 在Akka.NET中Actor都繼承于UntypedActor。/// </summary>
? ? public class BasicActor : UntypedActor
? ? {
? ? ? ? protected override void PreStart()
? ? ? ? {
? ? ? ? }
? ? ? ? protected override void PreRestart(Exception reason, object message)
? ? ? ? {
? ? ? ? }
? ? ? ? protected override void OnReceive(object message)
? ? ? ? {
? ? ? ? ? ? //handle messages here
? ? ? ? }
? ? ? ? protected override void PostStop()
? ? ? ? {
? ? ? ? }
? ? ? ? protected override void PostRestart(Exception reason)
? ? ? ? {
? ? ? ? }
? ? }
}
什么是Message
可能已經(jīng)注意到了BasicActor中定義的方法
.Net
protected override void OnReceive(object message) { //handle messages here}
該OnReceive方法是Actor接收Message的地方,在Akka中,一個Message就是一個Object,Message可以是任意類型的實(shí)例
Actor通常只能處理特定類型的Message,如果Actor收到無法處理的Message,則不會觸發(fā)任何異常,一般只是將Message標(biāo)記為"unhandled"
Message是不變的
什么是"不可變"的對象呢?
不可變對象:是一個對象中的狀態(tài)(即該對象在內(nèi)存中的內(nèi)容)一旦被實(shí)例化就不能修改
類似于String類型(.net中 string)
不可變的Message本質(zhì)上是線程安全的.沒有線程可以修改不可變Message的內(nèi)容,所以接收到原始Message的第二個線程不必?fù)?dān)心以前的線程有修改Message的可能
因此,在Akka中,所有消息都是不可變的,線程也是安全的,這也是為什么可以讓上千萬Akka的Actor同時處理消息的原因之一.正式不可變的Message消除了同步機(jī)制和其他不必要的代碼
Actor的行為
我們大概了解了Actor和Message,那這些如何用?
Actor通過Message進(jìn)行通信
在OOP中,對象通過函數(shù)調(diào)用與其他對象進(jìn)行通信.A類在B類上調(diào)用一個函數(shù),并等待該函數(shù)返回,然后A類可以繼續(xù)其余工作.在Akka和Actor模型中,Actor之間通過Message進(jìn)行通信
那有什么特別的呢?
對于初學(xué)者來說,消息傳遞是異步的,發(fā)送Message的Actor可以在接收的Actor處理發(fā)件人的Message時繼續(xù)執(zhí)行其他工作.所以Actor之間的互動,默認(rèn)情況下都是異步的
還有另一個變化
由于所有"函數(shù)調(diào)用"都被Message代替,即對象的不同實(shí)例,因此Actor可以存儲其函數(shù)調(diào)用的歷史記錄,甚至延遲處理一些函數(shù)調(diào)用
想象一下,如何使用一個Actor來構(gòu)建像Microsoft Word中的“撤消”按鈕這樣的功能?默認(rèn)情況下,你有一個Message代表每個人對文檔所做的更改.要撤消其中一個更改,只需要將消息從UndoActor的垃圾郵件中刪除,并將該更改推送回管理Word文檔的當(dāng)前狀態(tài)的另一個Actor。在實(shí)踐中這是一個非常強(qiáng)大的概念。
Actor發(fā)送Message到地址,而不是直接發(fā)送給Actor
位置透明
什么是位置透明,位置透明意味著無論在何時向Actor發(fā)送Message,都不需要知道他們在系統(tǒng)中的位置,可能這個Message覆蓋數(shù)百臺計(jì)算機(jī),所以只需要知道Actor的地址.
就像給別人打電話一樣,你只需要知道電話號碼,其他的電信供應(yīng)商就會幫你連通.
Actor的工作方式也是一樣,每個Actor都包含了以下部分地址
Protocol(協(xié)議):就像網(wǎng)絡(luò)上使用的Http和Https一樣,Akka支持多種傳輸協(xié)議用于進(jìn)程間的通信.單進(jìn)程Actor系統(tǒng)默認(rèn)協(xié)議是akka://如果使用的遠(yuǎn)程或者集群,則通常會使用akka.tcp://或者akka.udp://在節(jié)點(diǎn)之間進(jìn)行通信
ActorSystem(Actor系統(tǒng)):在akka的每個ActorSystem實(shí)例必須在啟動時賦予一個名稱,該名稱可以由所有參與分布式的多個進(jìn)程或者計(jì)算機(jī)共享ActorSystem.
Address(地址):如果不使用遠(yuǎn)程處理,則ActorPath可以省略這一部分,這部分是用來傳達(dá)用于Actor系統(tǒng)之間的遠(yuǎn)程通信的具體IP地址/域名和端口信息
Path(路徑):這是一個特定Actor在地址上的路徑,結(jié)構(gòu)就像一個網(wǎng)站的Url,所有用戶定義的actor都是在/user/下
因此,要向Actor發(fā)送消息:
.Net
//local actor
var actorRef = MyActorSystem.Selection("/user/myActor");
actorRef.Tell("HI!");
//remote actor
var remoteActorRef = MyActorSystem.Selection("akka.tcp://MyActorSystem@localhost:1001/user/myActor");
remoteActorRef.Tell("HI!");
向遠(yuǎn)程Actor發(fā)送Message就像本地Actor一樣,這就是位置透明的意思
發(fā)送給Actor地址的所有Message都是放置在屬于Actor的"郵箱"中
當(dāng)向Actor發(fā)送Message時,該Message不會直接進(jìn)入Actor的OnReceive方法.
Message被放置在按照FIFO(先進(jìn)先出)順序排列的"郵箱"中,就像C#中Queue<T>(java中LinkedList)數(shù)據(jù)結(jié)構(gòu)一樣.郵箱有一個非常簡單的工作,接收和掛起郵件,直到Actor準(zhǔn)備好處理它們.
當(dāng)Actor準(zhǔn)備處理Message時,郵箱將把Message推送到Actor的OnReceive方法中,并運(yùn)行Actor的消息處理方法.
Actor只能一次處理一個Message
Akka中保證參與者處理Message時,Actor的上下文和內(nèi)部狀態(tài)都是線程安全的
之所以這樣的原因是:
因?yàn)镸essage是不可變的,所以每個Message的內(nèi)容本質(zhì)上是線程安全的
因?yàn)镸essage是串行處理的,所以更改一個actor的內(nèi)部狀態(tài)和上下文都不需要跨多個線程進(jìn)行同步
因此,一個Actor在它的OnReceive方法退出之前,都無法處理下一個Message.當(dāng)處理完時,郵箱會將下一個可用的Message推送到OnReceive方法中
Actor可以擁有內(nèi)部狀態(tài)
就像任何類一樣,Actor可以擁有自己的屬性和字段
當(dāng)一個Actor重啟時,actor實(shí)例就像我們這個BasicActor類的一個實(shí)例一樣被銷毀并重新創(chuàng)建.
BasicActor創(chuàng)建新的一個實(shí)例,通過Props把構(gòu)造函數(shù)的參數(shù)傳遞給新的實(shí)例
Actor有一個明確的生命周期
在Actor可以從郵箱中開始處理Message之前,必須由Actor系統(tǒng)進(jìn)行實(shí)例化并運(yùn)行其生命周期.
?
?
?Actor被創(chuàng)建并啟動,然后會花費(fèi)大部分時間接收消息,如果不再需要Actor,可以終止或者停止Actor
如果Actor以外崩潰(即拋出未處理的Exception),Actor的父級將從頭開始自動重啟Actor的生命周期,而不會丟失仍在Actor郵箱中的剩余的Message
結(jié)合之前的例子BasicActor中實(shí)現(xiàn)這個生命周期:
Actor's constructor(構(gòu)造函數(shù)):BasicActor沒有中并沒有聲明,而是使用了默認(rèn)的構(gòu)造函數(shù),當(dāng)然也可以使用任何帶參數(shù)的構(gòu)造函數(shù)
PreStart:這是在actor可以開始接收消息之運(yùn)行,是放置初始化邏輯的好地方,在重啟時會被調(diào)用
PreRestart:如果Actor意外失敗(即拋出未處理的Exception),Actor的父級會重啟Actor
PostStop:一旦Actor停止并且不再接收消息,就會被調(diào)用,這里可以處理清理引用對象,PostStop在Actor重啟時不會調(diào)用,只有在人為關(guān)閉時才調(diào)用
PostRestart:在PreRestart之后,PreStart之前被調(diào)用,這是可以處理崩潰錯誤和診斷報告
?
?每個Actor都有父級,有的有子級
就像人一樣,Actor有父母,有的有祖父母,兄弟姐妹和孩子
這意味著每個Actor都必須由其他Actor創(chuàng)建,所以我們代碼:
.Net
var actorRef = MyActorSystem.ActorOf(Props.Create<BasicActor>(), "myActor"); actorRef.Tell("HI!");?
在/user/根Actor下創(chuàng)建一個新的Actor,新的Actor的路徑就是/user/myActor
同樣,還可以在BasicActor中創(chuàng)建其他Actor
.Net
protected override void OnReceive(object message) { var childActor = Context.ActorOf(Props.Create<BasicChildActor>(), "child1");childActor.Tell("Hi!"); }這樣childActor的路徑就是/user/myActor/child1/
父級監(jiān)督子級
在關(guān)于Actor生命周期的部分中,提到了"Actor是由他們的父級進(jìn)行重啟"的概念.這就類似于,每個家長都收到他們孩子發(fā)的特別的消息"求助,我要崩潰了".
每個父級都帶有默認(rèn)的SuperviserStrategy對象(可以自定義).該對象決定了如何處理他們的子級Actor的失敗.有三種方式:
Restart:重啟失敗的Actor,父級默認(rèn)方式,除非子級在60秒內(nèi)反復(fù)重啟
Stop:永久停止失敗的Actor
Escalate:將決定交給父級的父級處理
當(dāng)發(fā)出一個Restart或者Stop 的Message,受影響的所有的子級包括自己都會重啟或者停止.當(dāng)然,也可以重啟失敗的Actor的actor家族樹整個部分?
引薦:https://petabridge.com/blog/akkadotnet-what-is-an-actor/
原文地址:http://www.cnblogs.com/yangleiblog/p/6766197.html
.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Akka系列---什么是Actor的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 世界上最大的搜索引擎公司 Google
- 下一篇: .NET的一点历史故事:擦肩而过的机遇