此篇文章基本上是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

 
- BecomeStacked
 直接變更
- Become
 
Written with StackEdit.
