天天看點

akka設計模式系列(Actor模型)

  談到Akka就必須介紹Actor并發模型,而談到Actor就必須看一篇叫做《A Universal Modular Actor Formalism for Artificial Intelligence 》的論文,它最早發表于1973年,提出了一種并發計算的理論模型,Actor就源于該模型。

  在Actor模型中,actor是一個并發原語,簡單的說,一個actor就是一個勞工,與程序或線程一樣都能夠工作或處理任務。其實這還有點不好了解,我們可以把它想象成面向對象程式設計語言中的一個對象執行個體。在OOP中一個對象可以通路或修改另一個對象的屬性,也可以直接調用另一個對象的方法。例如下圖,person1給person2發送了一個消息,直接調用方法就行了。深入底層執行邏輯的話,結果就是JVM轉到sayHello的代碼區,一步步執行。

public class HelloWorld {
    private String name = "";
    public HelloWorld(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
    public void sayHello(HelloWorld to, String msg){
        System.out.println(to.getName()+" 收到 "+name+" 的消息:"+ msg);
    }
}

public class OOPInvoke {
    public static void main( String[] args ) {
        HelloWorld person1 = new HelloWorld("Person1");
        HelloWorld person2 = new HelloWorld("Person2");
        person1.sayHello(person2,"Hello world");
    }
}      

   sayHello在一個線程中執行基本沒有問題,但是多個線程執行時,就可能出問題了,因為在執行sayHello的時候person2的name值可能被其他線程修改。這是一個name字段,意外修改沒有關系,但如果是一個金額字段呢?

  actor和對象的不同之處在于,actor的狀态不能直接讀取、修改,actor的方法不能直接調用。actor隻能通過消息傳遞的方式與外界通信。每個對象都有一個this指針,代表對象的位址,可以通過該位址調用方法或存取狀态;與此類似,actor也有一個代表本身的位址,但隻能向該位址發送消息。

  簡單點說,actor通過消息傳遞的方式與外界通信。消息傳遞是異步的。每個actor都有一個郵箱,該郵箱接收并緩存其他actor發過來的消息,actor一次隻能同步處理一個消息,處理消息過程中,除了可以接收消息,不能做任何其他操作。前面這段話,我加粗、傾斜、加下劃線、還用紅色字型标出,你就應該知道有多重要了,這就是actor模型的本質。

akka設計模式系列(Actor模型)

  Actor模型的另一個好處就是可以消除共享狀态,因為它每次隻能處理一條消息,是以actor内部可以安全的處理狀态,而不用考慮鎖機制。标紅的這兩句話如果可以了解透徹,基本上Actor模型的精髓你就掌握了。

  那麼讀者可能會問,每次隻處理一個消息,這不是會嚴重的影響性能麼?廢話,一次處理一個消息當然影響性能了。不過,如果你恰當的運用Akka和Actor模型,完全可以不必關心性能的問題。下面是Actor模型的幾個基本原則:

       (1)所有的計算都是在actor中執行的

       (2)actor之間隻能通過消息進行通信交流

       (3)為了響應消息,actor可以進行下列操作

               a.  更改狀态或行為

               b. 發消息給其他actor

               c. 建立有限數量的子actor

  看了上面幾個基本原則,你是不是更加鑒定的認為Actor模型沒啥用?嗯,這就對了,因為我當初也是這麼認為的。一次處理一個消息,沒有并發,怎麼提高性能;如果actor隻能更改狀态或行為,發消息給其他actor,建立有限數量的子actor,那我的業務邏輯在哪裡;actor之間隻能通過消息通信,我怎麼知道另外一個actor的位址。其實吧,如果你能問到這幾個問題,那麼恭喜你,你非常需要我這個部落格系列,我會一一進行分析,把我之前的坑展示給你看,以確定你不會再掉進去。

  其實Actor模型出現的很早,而20世紀80年代,愛立信在Erlang中實作了Actor模型,用于嵌入式電信應用程式。該實作中引入了監督機制提供的容錯性概念。愛立信使用Erlang和Actor模型實作了一款日後經常被提及的應用:AXD301。這玩意兒能提供99.9999999% 的可用性,看到沒,7個9!!!絕對可以亮瞎人們的狗眼,這意味着在100年的時間中,AXD301隻有3.1秒的時間會當機。

  Actor模型的另一個重要的特性就是容錯,它通過監督機制提供容錯。這跟java中的throw exception有點類似,都是把處理響應錯誤的責任交給出錯對象以外的實體。但在java中如果一個程式或者線程抛出了一個異常,你敢放心的恢複對應的程式或線程嗎?你確定恢複之後還能正常的運作嗎,畢竟需要很多資源需要重新建立。但Actor模型可以!

  

akka設計模式系列(Actor模型)

  如上圖所示actor之間是有層級關系的,子actor如果出現了異常會抛給父actor,父actor會根據情況重新建構子actor,子actor從出現異常,到恢複之後正常運作,這段時間内的所有消息都不會丢失,等恢複之後又可以處理下一個消息。也就是說如果一個actor抛出了異常,除了導緻發生異常的消息外,任何消息都不會丢失。這容錯性當然好了。當然了,為了實作這種特性,akka或者Erlang需要做很多工作的。

  Akka中的Actor模型還有另外一個比較重要的兩個特性:分布式與位置透明性。其實可以認為這是一個特性。Actor模型中一個很重要的概念就是actor位址,因為其他actor需要通過這個位址與actor進行通信。akka考慮到分布式的網絡環境,對actor位址進行了抽象,屏蔽了本地位址和遠端位址的差異,對于開發者來說基本上是透明的。由于actor位址是透明的,那麼akka有引入了叢集。當然了基于Actor模型和位置透明性,Akka還有其他很多有用的元件,這裡就不介紹了,後面會詳細說明。

  關于Actor模型就先介紹到這裡,下一章節我們會介紹Actor模型的最基本的設計模式,以說明該模型的适用場景。