AKKA 與Actor 簡單入門

不知道從什麼時候響應式編程變得特別流行,我翻閱了很多資料,發現也沒有一本書和一個版塊能把這件事說清楚的,但是有一個網站發起了《響應式宣言》,裡面大致羅列了設計響應式系統的設計準則,總結了下有以下四點:

1、靈敏性

只要有可能,系統就會及時響應。靈敏性是可用性和效用的基石,但還不止於此,靈敏性還意味著問題能夠被更快地偵測到並得到有效地處理。靈敏的系統著眼於提供迅速和一致的響應時間,建立可靠的服務上限,因而它們可以交付一致的服務質量。這種一致的行為反過來又能簡化出錯處理,建立最終用戶對系統的信心,並且促使他們與系統做進一步交互。

2、伸縮性

系統在變化的工作負載下保持靈敏。響應式系統能夠對輸入速率的變化做出反應,通過增加或減少資源分配的方式服務這些輸入。這意味著系統在設計上不存在爭用點或中心的瓶頸,進而讓共享或複製的模塊有能力應對,並可以把輸入分發給這些模塊。通過提供相關的實時性能測量數據,響應式系統既支持預測式量級擴展算法,也支持響應式的量級擴展算法。響應式系統可以在商業的硬件和軟件平臺上以經濟實惠的方式實現可伸縮性。

3、容錯性

系統在面臨故障時也能保持靈敏度。可回覆性不僅適用於高可用的關鍵任務系統——任何系統都可以具有這種屬性,一個不具可回覆性的系統一旦出現故障,就會變得不靈敏。可回覆性可以通過複製,圍控,隔離和委派等方式實現。在可回覆性的系統中,故障被包含在每個組件中,各組件之間相互隔離,從而允許系統的某些部分出故障並且在不連累整個系統的前提下進行恢復。每個組件的恢復過程都可委派給另一個(外部的)組件來完成,並且在必要的位置可藉助複製機制來確保系統的高可用性。這樣一來,組件的客戶就不必為處理組件自身的故障而承擔壓力。

4、事件驅動/消息驅動

響應式系統依賴異步的消息傳遞建立組件之間的界限,這一界限確保了松耦合,隔離,位置透明性等特性的實現,還提供了以消息的形式把故障委派出去的手段。利用顯式的消息傳遞,可以通過建立和監視消息隊列的方式實現負載管理、可伸縮性以及流程控制,還可以在必要時應用背壓機制。位置透明的消息傳遞作為一種溝通手段,使得故障管理可以在相同的構造和語義下工作,無論實際的工作環境是跨集群的環境還是一個單獨的主機。非阻塞溝通則允許消息接收者僅在激活的狀態下消費資源,導向更少量的系統開銷。

AKKA 與Actor 簡單入門

大系統由較小的系統構成,因此勢必依賴於這些構成要素的響應式屬性。這意味著響應式系統需要應用一定的設計原則,使這些屬性在所有不同等級的構成要素上都適用,進而使它們可組合。世界上最大的系統所依賴的體系結構,都是基於這些屬性構建的,它們每天都在服務數十億人的需求。因此只要滿足響應式準則設計的系統,從理論上來說都不會有太大問題。

Actor Model

在1973年卡爾休伊特在一篇論文中首次提出了Actor,它是一種並行計算的模型 ;我們知道在使用Java語言進行併發編程時,需要特別關注共享的數據結構,線程間的資源競爭容易導致死鎖等問題,而Actor模型便是要解決線程和鎖帶來的問題,Actor是一種基於事件(Event-Based)的輕量級線程,在使用Actor進行併發編程時只需要關注代碼結構,而不需要過分關注數據結構,因此Actor最大限度地減少了數據的共享。

Actor 特別像一種生產者和消費者模式,它的流程如下圖所示:

AKKA 與Actor 簡單入門

兩個Actor之間的通信是基於消息的,生產者的Actor將消息發送至郵箱,消費者的Actor從郵箱中消費消息。郵箱的底層實現是類似於FIFO隊列。

一個Actor包含了三樣東西,分別是行為、狀態、消息。當一個Actor接收到消息的時候,它可以做三件事,分別是 :

1、創建另外一些Actors

2、向已知的Actors發消息

3、指定接收下一條消息時的行為

Actor是並行計算的最小顆粒:

•單個Actor的狀態和行為只由接收到的消息驅動

•單個Actor串行地處理接收到的消息

• 單個Actor總是線程安全的 •

•大量Actors同時處在活躍狀態,其行為是並行的

•並行是多個Actors的行為

那麼使用Actor進行編程到底有什麼好處呢? 它跟我們傳統的面向對象編程有什麼區別,我們以餐館點餐為例來說明一下:

假設我們有三個Actor,分別代表客戶、服務員、廚師

1.有一家餐館,客人向服務員點單。

2.服務員將客人的菜品放在廚師的郵箱中 (廚師郵箱)

3.廚師獲取這條消息,製作壽司(處理消息)

4.壽司準備好後,發一條消息給服務員郵箱(服務員郵箱,此時廚師可以給其他人做菜)

5.當服務員有空的時候,獲取食物的消息,將其送到客人的餐桌(客戶郵箱)

6.客人準備好的時候,就會吃壽司(處理消息)

我們從上面的例子中可以看出,每一步都是異步的,每個人都各司其職,如果採用傳統的Java做法,可能每一步都是同步的,那麼性能可想而知比較差,如果考慮到最壞的情況,每一步的狀態都加鎖的話,性能就更差了。從這個例子中可以看出Actor的威力了嗎,它其實就源自我們的生活。

Actor實現

現在有很多種語言都實現了Actor,scala在Java的基礎上添加了一些語法糖,功能不太強大,但是AKKA基於scala重寫了scala actor,功能強大。ErLang語言在底層就實現了Actor,功能強大,大名鼎鼎的 rabbitmq就是基於erLang實現的。Go語言發展比較迅猛,它的實現方式通過模擬協誠的方式。


AKKA的 Actor實現

在AKKA中Actor 是非常輕量的計算單元,在單機單核本地上 有5000萬/秒消息轉發能力。1GB內存能生成 250萬個Actors;

在AKKA中Actor 位置透明,本身既具分佈能力它可以按地址創建和查找本地或遠程節點,訪問本地或遠程節點僅在於地址不同, 可以跨節點遷移;

在AKKA中Actor 是按層級實現督導,Actor按樹狀組織成層級,父Actor監控子Actor的狀態,可以在出狀況時停止、重啟、恢復。

使用AKKA

可以通過添加lib包或者通過maven的方式來添加AKKA的支持,個人比較推薦第二種方法,我們以2.5.11版本為例,首先在工程中添加如下maven配置

com.typesafe.akka

akka-actor_2.12

2.5.11

我們新建一個接收消息的Actor:

public class AkkaDb extends AbstractActor {protected final Map map = new ConcurrentHashMap<>();@Overridepublic PartialFunction receive() {return super.receive();}public Receive createReceive() {return ReceiveBuilder.create().match(SetRequest.class, message -> {map.put(message.getKey(), message.getValue());})}).matchAny(o -> sender().tell(new Status.Failure(new ClassNotFoundException()), self())).build();}}

這個actor做的事情特別簡單,就是收到一條消息,將消息保存在map中,來模擬kv 數據庫的用法,如果找不到,會報一個classNotFound error,AbstractActor是基於java 8的api,繼承這個類之後,必須要實現createReceive 這個方法,match 是用來匹配消息的,在本例中匹配的是 SetRequest 這個類型的消息,當收到這條消息的時候,我們就把消息存入map;

public class SetRequest implements Serializable{private final String key; private final Object value; public SetRequest(String key, Object value) {this.key = key; this.value = value;}public String getKey() {return key;}public Object getValue() {return value;}}

下面的例子演示瞭如何向這個Actor發送消息:

//創建actorSysytem,是所有actor的根對象ActorSystem actorSystem = ActorSystem.create();//actor的引用ActorRef akkadbRef = actorSystem.actorOf(Props.create(AkkaDb.class));SetRequest setRequest = new SetRequest("phone","159xxxxx");akkadbRef.tell(setRequest,ActorRef.noSender());

在Akka中創建Actor必須通過 ActorSystem,ActorSystem是Actor的根對象,在實際生產中建議使用全局單例的形式; 在本例子中我們通過actorSystem創建了 一個Actor的一個引用,通過這個引用就可以給actor發送消息了。我們發送了手機號是15xxx的一條消息,tell是AKKA發送消息的一種方式,是不需要等待回覆結果的,第一個參數是發送消息的對象,第二個參數指定是由誰發送的,如果不想指定發送者,建議使用 noSender() 或者self()的形式。

今天就先簡單介紹到這裡,後續我們詳細介紹AKKA發送消息的幾種方式,例如:ask 類似於java 的future; tell 不需要回復結果的,性能最高; 如果有疑問的話可以留言給我,喜歡的話點擊一下關注,我們會不定期每天推送一篇技術文章給你,感謝...


分享到:


相關文章: