Akka系列(一):Akka簡介與Actor模型

ScalaCool發表於2017-03-23

本文由 GodPan 發表在 ScalaCool 團隊部落格。

Akka是一個構建在JVM上,基於Actor模型的的併發框架,為構建伸縮性強,有彈性的響應式併發應用提高更好的平臺。本文主要是個人對Akka的學習和應用中的一些理解。

Actor模型

Akka的核心就是Actor,所以不得不說Actor,Actor模型我通俗的舉個例子,假定現實中的兩個人,他們只知道對方的地址,他們想要交流,給對方傳遞資訊,但是又沒有手機,電話,網路之類的其他途徑,所以他們之間只能用信件傳遞訊息,很像現實中的的郵政系統,你要寄一封信,只需根據地址把信投寄到相應的信箱中,具體它是如何幫你處理送達的,你就不需要了解了,你也有可能收到收信人的回覆,這相當於訊息反饋。上述例子中的信件就相當於Actor中的訊息,Actor與Actor之間只能通過訊息通訊。當然Actor模型比這要複雜的多,這裡主要是簡潔的闡述一下Actor模型的概念。

Akka中Actors模型

  • 對併發模型進行了更高的抽象
  • 非同步、非阻塞、高效能的事件驅動程式設計模型
  • 輕量級事件處理(1GB記憶體可容納百萬級別個Actor)

為什麼Actor模型是一種處理併發問題的解決方案?

一開始我也不怎麼理解,腦子裡的一貫思維是處理併發問題就是如何保證共享資料的一致性和正確性,為什麼會有保持共享資料正確性這個問題呢?無非是我們的程式是多執行緒的,多個執行緒對同一個資料進行修改,若不加同步條件,勢必會造成資料汙染。那麼我們是不是可以轉換一下思維,用單執行緒去處理相應的請求,但是又有人會問了,若是用單執行緒處理,那系統的效能又如何保證。Actor模型的出現解決了這個問題。

Actor模型概圖

Actor模型

從上圖中我們可以看到,Actor與Actor之前只能用訊息進行通訊,當某一個Actor給另外一個Actor發訊息,訊息是有順序的,你只需要將訊息投寄的相應的郵箱,至於對方Actor怎麼處理你的訊息你並不知道,當然你也可等待它的回覆。

JVM中的Actor有以下幾個特點:

  • 每個Actor都有對應一個郵箱
  • Actor是序列處理訊息的
  • Actor中的訊息是不可變的

其實只從上面一些描述來看,並不能看出Actor在處理併發問題上的有什麼優勢。

但我總結了兩點:簡化併發程式設計提升程式效能

1.簡化併發程式設計:

我們一開始說過併發導致最大的問題就是對共享資料的操作,我們在面對併發問題時多采用的是 用鎖去保證共享資料的一致性,但這同樣也會帶來其他相關問題,比如要去考慮鎖的粒度(對方法,程式塊等),鎖的形式(讀鎖,寫鎖等)等問題,這些問題對併發程式來說是至關重要的,但一個初寫併發程式的程式設計師來說,往往不能掌控的很好,這無疑給程式設計師在程式設計上提高了複雜性,而且還不容易掌控,但使用Actor就不導致這些問題,首先Actor的訊息特性就覺得了在與Actor通訊上不會有共享資料的困擾,另外在Actor內部是序列處理訊息的,同樣不會對Actor內的資料造成汙染,用Actor編寫併發程式無疑大大降低了編碼的複雜度。

2.提升程式效能:

我們之前說過既然用單執行緒處理,那如何保證程式的效能?首先Actor是非常輕量級的,你可以再程式中建立許多個Actor,而且Actor是非同步的,那麼如何利用它的這個特性呢,我們要做的就是把相應的併發事件儘可能的分割成一個個小的事件,讓每個Actor去處理相應的小事件,充分去利用它非同步的特點,來提升程式的效能。

其實Scala中原生的Actor並不能完成很多事,不是一套完整的併發解決方案,不適合用於生產環境,比如錯誤恢復,狀態持久化等,所以在較新版本的Scala類庫中,Akka包已經取代了原生的Actor。

Akka

那下面我們來簡單說說Akka吧,Akka作為一套成熟的併發解決方案,已經被業界大量採用,尤其是在金融,遊戲等領域,Akka中的容錯機制,持久化,遠端呼叫,日誌等都是很重要的模組,這些內容都會在這個系列的後續文章裡一一講解。下面就以一個入門Akka程式來結束本篇文章吧。現在我們假設有一個家居機器人,我們只需要給它傳送訊息它便會幫我們處理相應的事情,現在我們用程式來模擬這個場景:原始碼連結

本示例使用Scala語言,構建工具為SBT,IDE為IntelliJ IDEA.

1.首先建立一個基於SBT的Scala工程

build.sbt配置:

name := "Example_01"

version := "1.0"

scalaVersion := "2.11.8"

val akkaVersion   = "2.4.16"

libraryDependencies +=
  "com.typesafe.akka" %% "akka-actor" % akkaVersion
複製程式碼

2.我們來定義一些訊息:

trait Action{
  val message: String
  val time: Int
}

case class TurnOnLight(time: Int) extends Action {   // 開燈訊息
  val message = "Turn on the living room light"
}

case class BoilWater(time: Int) extends Action {   // 燒水訊息
  val message = "Burn a pot of water"
}
複製程式碼

3.我們利用Actor來實現一個模擬機器人:

class RobotActor extends Actor {
  val log = Logging(context.system, this)
  def receive: Receive = { //機器人接受指令
    case t: TurnOnLight => log.info(s"${t.message} after ${t.time} hour")
    case b: BoilWater => log.info(s"${b.message} after ${b.time} hour")
    case _ => log.info("I can not handle this message")
  }
}
複製程式碼

4.我們去測試這個機器人:

object Example_01 extends App {
  val actorSystem = ActorSystem("robot-system") 
  val robotActor = actorSystem.actorOf(Props(new RobotActor()), "robotActor") //建立一個機器人
  robotActor ! TurnOnLight(1) //給機器人傳送一個開燈命令
  robotActor ! BoilWater(2) //給機器人傳送一個燒水命令
  robotActor ! "who are you" //給機器人傳送一個任意命令
  actorSystem terminate ()
}

複製程式碼

5.執行結果

[INFO] [03/19/2017 13:48:05.622] [robot-system-akka.actor.default-dispatcher-4] [akka://robot-system/user/robotActor] Turn on the living room light after 1 hour
[INFO] [03/19/2017 13:48:05.622] [robot-system-akka.actor.default-dispatcher-4] [akka://robot-system/user/robotActor] Burn a pot of water after 2 hour
[INFO] [03/19/2017 13:48:05.622] [robot-system-akka.actor.default-dispatcher-4] [akka://robot-system/user/robotActor] I can not handle this message
複製程式碼

上面是一個非常簡單的Akka例子,我們首先建立了一個機器人的Actor,然後通過向它傳送不同指令,讓它根據指令去做相應的事情,大家可以自己嘗試去寫一寫相似的例子。

這篇就先到這裡了,下一篇主要給大家講講Akka中Actor的分層結構。

Akka系列(一):Akka簡介與Actor模型

相關文章