此篇文章基本上是Akka.Net的基礎教學的筆記。
什麼是Actor?
Actor是一個系統中對參與者的模擬。它可能是一個實體,一個類別,可以做一些事情,及互相溝通。
Actors如何互相溝通?
Actors透過訊息互相溝通,任何POCO都可以是訊息,字串、整數、類別、或實作了一個介面的物件。
//this is a message!
public class SomeMessage
{
public int SomeValue {get; set}
}
傳送訊息:
//send a string to an actor
someActorRef.Tell("this is a message too!");
Actor可以做什麼?
除了處理收到的訊息外,一個actor還可以:
* 建立另一個actor
* 傳送訊息給另一個actor
* 變更它自己的行為,並處理不同的訊息(相對於原先的行為的訊息)
什麼是一個ActorSystem
一個ActorSystem
是對Akka.NET framework內部系統的一個參考,所有的actor都生存在其中,你也需要以它來建立你的actor。
安裝Akka.Net NuGet
Install-Package Akka
建立ActorSystem
var system = ActorSystem.Create("MyActorSystem");
建立一個Actor
var writerActor = system.ActorOf(
Props.Create(
() => new WriterActor()),
"writerActor");
或是使用泛型的方式:
var writerActor = system.ActorOf(
Props.Create<WriterActor>(), "writerActor");
Props
和IActorRef
’s
什麼是Props
?
Props
是一個封裝了所有需要建立實體actor的資訊的設定類別。
如何建立Props
- 使用lambda語法:
Props props = Props.Create(() => new MyActor(..), "...");
- 使用泛型語法:
Props props = Props.Create<MyActor>();
什麼是IActorRef
IActorRef
代表對一個actor的參考。目的是支援透過ActorSystem
傳送訊息給一個actor。
子Actor,Actor階層及監管
建立最上層的actors:
// create the top level actors from above diagram
IActorRef a1 = MyActorSystem.ActorOf(Props.Create<BasicActor>(), "a1");
IActorRef a2 = MyActorSystem.ActorOf(Props.Create<BasicActor>(), "a2");
建立a2下的子actors:
// create the children of actor a2
// this is inside actor a2
IActorRef b1 = Context.ActorOf(Props.Create<BasicActor>(), "b1");
IActorRef b2 = Context.ActorOf(Props.Create<BasicActor>(), "b2");
Actor階層中如何監管?
每個actor會監管其子actor,且僅止於其子actor,如上面的階層圖,a2僅監管b1及b2。
何時需要監管?
發生錯誤時!
當一個子actor發生未處理的例外且要掛掉時,它會尋求它的父actor的幫忙。它會傳送一個Failure
類別的訊息,並由父actor決定如何處理。
監管規則
當父actor收到來自其子actor的錯誤訊息,它可以決定以下列方式處理:
* 重啟子actor(預設行為)
* 停止子actor
* 上報錯誤(且停止自己的動作)
監管策略
有兩個內建的策略型態:
* 一對一:父actor發佈的命令僅針對發生錯誤的子actor
* 一對全部:父actor發佈的命令不僅只針對發生錯誤的子actor,而是其所有的子actor
重點是什麼?隔離!
如下圖所示:
我們切分工作,把可能會發生錯誤的地方放在某一節點上,這讓潛在的錯誤被隔離,系統不會因為一個節點的錯誤而崩潰!
使用ActorSelection
依位址尋找特定Actor
var selection = Context.ActorSelection("/path/to/actorName");
selection.Tell(message);
或使用ActorOf:
class FooActor : UntypedActor {}
Props props = Props.Create<FooActor>();
// the ActorPath for myFooActor is "/user/barBazActor"
// NOT "/user/myFooActor" or "/user/FooActor"
IActorRef myFooActor = MyActorSystem.ActorOf(props, "barBazActor");
// if you don't specify a name on creation as below, the system will
// auto generate a name for you, so the actor path will
// be something like "/user/$a"
IActorRef myFooActor = MyActorSystem.ActorOf(props);
也可參考:When Should I Use Actor Selection?
Actor的生命週期
使用ReceiveActor
來更聰明的處理訊息
public class StringActor : ReceiveActor
{
public StringActor()
{
Receive<string>(
s => s.StartsWith("AkkaDotNet"),
s =>{ /* handle string */ });
Receive<string>(
s => s.StartsWith("AkkaDotNetSuccess"),
s =>{/* handle string*/});
}
}
使用BecomeStacked
和UnbecomeStacked
在執行時變更Actor的行為
public class UserActor : ReceiveActor {
private readonly string _userId;
private readonly string _chatRoomId;
public UserActor(string userId, string chatRoomId) {
_userId = userId;
_chatRoomId = chatRoomId;
// start with the Authenticating behavior
Authenticating();
}
protected override void PreStart() {
// start the authentication process for this user
Context.ActorSelection("/user/authenticator/")
.Tell(new AuthenticatePlease(_userId));
}
private void Authenticating() {
Receive<AuthenticationSuccess>(auth => {
Become(Authenticated); //switch behavior to Authenticated
});
Receive<AuthenticationFailure>(auth => {
Become(Unauthenticated); //switch behavior to Unauthenticated
});
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// can't accept message yet - not auth'd
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// can't send message yet - not auth'd
});
}
private void Unauthenticated() {
//switch to Authenticating
Receive<RetryAuthentication>(retry => Become(Authenticating));
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// have to reject message - auth failed
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// have to reject message - auth failed
});
}
private void Authenticated() {
Receive<IncomingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// print message for user
});
Receive<OutgoingMessage>(inc => inc.ChatRoomId == _chatRoomId,
inc => {
// send message to chatroom
});
}
}
什麼是Switchable behavior?
在Actor Model中,一個Actor的核心屬性是它可以在訊息的處理過程中變更自己的行為。
這個特性讓Actor可以實作有限狀態機,或根據所收到的訊息變更處理的方式。
如何實作
堆疊式
- BecomeStacked
- UnbecomeStacked
直接變更
- Become
Written with StackEdit.