Akka 使用系列之一: 快速入門

UXGFos 7年前發布 | 16K 次閱讀 AKKA

Akka 是 Spark 實現內部通訊的組件,Spark 啟動過程的第一步便是建立 Akka 的 ActorSystem。因此看了有幾篇文章學習了下 Akka 相關知識。

1 Actor 模型和 Akka 簡介

Actor 模型由 Carl Hewitt 于上世紀70年代早期提出,目的是為了解決分布式編程中一系列的編程問題。Actor 的要點包括:Actor 是一個個相互之間獨立的實體; Actor 可以通過消息來通信,一個 Actor 收到其他Actor的信息后,可以根據需要作出各種相應反應;消息的類型可以是任意的,消息的內容也可以是任意的;當一個 Actor 收到多個消息時,它先建立一個消息隊列,將接收到的消息就放入隊列,每次從隊列中取出一個消息體進行處理。

Akka 是一個用 Scala 編寫的庫,用于簡化編寫容錯的、高可伸縮性的 Actor 模型應用。Akka 使得開發人員可以更輕松地開發具有容錯性、可擴展性和跨平臺的并發程序,在工業界得到了廣泛應用。

2 Akka 入門

下面我們將從一個學生老師例子出發,快速入門 Actor 模型。例子是從博客 rerun 拿來的 (我就是看這個博客學習 Akka 的,強烈推薦英語好的同學看看)。這個例子有兩個角色,一個是勤學好問的學生,一個是睿智的老師。每天早上,學生都會給老師發送一封郵件,向老師請教問題;而老師看到郵件之后,通過郵件給學生發送答案。在這個過程中,有幾點需要注意:

1. 郵件一旦發送,就不能改變;

2. 學生和老師都按照自己的工作節奏檢查郵箱;

3. 學生發送郵件之后,可以不等老師的回復。即工作流程可以是阻塞,也可以是非阻塞;

Akka 用于簡化編寫容錯的、高可伸縮性的 Actor 模型應用,即我們很容易用 Akka 實現上述學生老師的 Actor 模型。我們先建立老師 Actor, 將老師 Actor 實現成一個服務(我是不是想歪了),代碼如下所示。

class TeacherActor extends Actor {
  def receive = {
    case "1+1等于多少?"           => {
      val originalSender = sender;//
      sender ! "1+1等于2"
    }
    case "歷史上規模最大的眾籌行動是什么?" => {
      val originalSender = sender;//
      sender ! "歷史上規模最大的眾籌行動是 +1s"
    }
    case "騰訊第一網紅是誰?"     => {
      val originalSender = sender;//
      sender ! "騰訊第一網紅是\"我去\""
    }
    case _              => {
      val originalSender = sender;//
      sender ! "這個問題,老師也得查查書"
    }
  }
}

object TeacherServices extends App { 

  val config = ConfigFactory
              .parseResources("lietal.conf")
              .getConfig("RemoteServerSideActor")
  //讀入配置
  val system = ActorSystem("TeacherService",  config)
  //使用配置,建立 Actor 模型系統 ActorSystem。
  //ActorSystem 是訪問 Actor 模型系統的接口,類似于 Spark 的 SparkContext。

  system.actorOf(Props[TeacherActor], "teacherActor") 
  //創建TearcherActor,返回一個引用
  //teacherActor 是 Actor 的名,客戶端需要用
}

然后我們建立學生 Actor, 將學生 Actor 作為客戶端。

class StudentActor extends Actor{
  val path = "akka.tcp://remote-system@127.0.0.1:4999/"
              +"user/teacherActor" 
  // 遠程Actor的路徑,通過該路徑能夠獲取到遠程Actor的一個引用
  val remoteServerRef = context.actorSelection(path) 
  // 獲取到遠程Actor的一個引用,通過該引用可以向遠程Actor發送消息

  def receive = {
      case res:String => {
        println (res)
        //打印出從老師 Actor 獲得的答案
      }
      case time:Long => {
        remoteServerRef ! "歷史上規模最大的眾籌行動是什么?";
      }
    }
}

object StudentSimulator extends App{
  val config = ConfigFactory
               .parseResources("lietal.conf")
               .getConfig("RemoteClientSideActor")
  //讀入客戶端配置
  val actorSystem = ActorSystem("StudentClient",  config);
  //使用配置,建立 ActorSystem
  val studentActor = actorSystem.actorOf(Props[StudentActor])
  //獲得 StudentActor 的一個引用。
  //在程序中 Actor 不能直接被訪問。
  //所有操作都必須通過 ActorRef 引用。

  while(true){
      studentActor ! 7.toLong    // 7 點起床。。
      Thread.sleep(5000) // 假裝一天過去了
  }
}

不管是服務端還是客戶端,程序開始都從 lietal.conf 配置文件讀入相應的配置。其中服務端讀入 RemoteServerSideActor 的配置,而客戶端讀入 RemoteClientSideActor 的配置。lietal.conf 配置文件放在資源目錄 src/main/resources,以便打包時打入包內。lietal.conf 的內容如下所示。

RemoteServerSideActor {
  akka {
    actor {
      provider = "akka.remote.RemoteActorRefProvider"
    }
    remote {
      enabled-transports = ["akka.remote.netty.tcp"]
      netty.tcp {
        hostname = "127.0.0.1"
        port = 4999
      }
    }
  }
}

RemoteClientSideActor {
  akka {
    actor {
      provider = "akka.remote.RemoteActorRefProvider"
    }
    remote {
      enabled-transports = ["akka.remote.netty.tcp"]
      netty.tcp {
        hostname = "127.0.0.1"
        port = 5000
      }
    }
  }
}

從配置文件來看,如果我們在服務端啟動兩個老師 Actor, 他們會共用一個端口。我們很容易理解,所有發往老師 Actor 的消息都發往了服務器的一個端口,Akka 內部有一套機制將消息分發到不同的 Actor 中。這套機制就是 Akka 的 dispatcher,負責分發不同的消息到不同的 Actor。

完整項目代碼已經上傳到 Github 上了,需要的同學自取。將完整項目打包之后,分別以 TearcherService 和 StudentClient 為主類運行程序,老師 Actor 和學生 Actor 之間的通信就運行起來了。下面是老師服務端運行的結果。

下面是學生客戶端運行的結果。

3 總結

一開始我只想實現一個單機版本的老師學生 Actor,實現之后發現不能體現 Akka 的特點,因此又實現一個網絡版的老師學生 Actor。實現完網絡版之后,稍微加深了對 Actor 之間消息傳遞的理解。

 

 

來自:http://www.algorithmdog.com/akka-usage-getstarted

 

 本文由用戶 UXGFos 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!