簡體   English   中英

如何建立一個簡單的akka​​演員系統?

[英]How to model a simple akka actor system?

這是一個關於如何模擬akka演員的更一般的設計問題。 情況就一個非常簡單的例子進行了解釋,我想對可能性和方法及其優缺點進行更全面的回答。

有一個用戶對象,應該用它做一些事情。 讓我們說: doSomethingWithUser(user: User) 假設用戶有一個屬性avatar: Option[String]這是一個url。 如果存在,則應在運行doSomethingWithUser方法之前抓取URL后面的實際圖像。 在談論Akka,我會創建一個Actor DoSomethingWithUserActor ,它可以接收兩條消息:

case class NewUser(user: User)
case class NewUserWithImage(user: User, imageData: Array[Byte])

FetchImageActor圖像數據實現為Actor FetchImageActor ,它可以處理一條消息:

case class FetchImage(url: String)

並產生一條消息:

case class GotImage(imageData: Array[Byte])

MainActor是根actor,只接收一條消息NewUser ,其處理方式如下:

def receive {
  case newUser: NewUser => {
    newUser.avatar match {
      case Some(avatar) => {
        // here I would like to send a message to the FetchImageActor, 
        // wait for the response (GotImage) and once it's there send a
        // NewUserWithImage message to the DoSomethingWithUser actor.
        //
        // How can this be done?
        // Is it a good idea to use a Future here, and if so, how can this
        // be done?
        //
        // pseudocode:
        val gotImage: GotImage = // get it somehow
        doSomethingWithUserActor ! NewUserWithImage(newUser.user, gotImage.imageData)
      }
      case _ => doSomethingWithUserActor forward NewUser(newUser.user)
  }
}

DoSomethingWithUserActor處理消息NewUserNewUserWithImage 也許是這樣的:

def receive {
  case newUser: NewUser => doSomethingWithUser(newUser.user)
  case newUserWithImage: NewUserWithImage => {
    doSomethingWithImage(newUserWithImage.imageData)
    doSomethingWithUser(newUser.user)
  }
}

private def doSomethingWithUser(user: User) = { ... }
private def doSomethingWithImage(imageData: Array[Byte]) = { ... }

首先我不知道,如果用戶有一個頭像,如何進行異步調用,其次,我不知道這是否一般是一種以這種方式處理這個問題的好方法。

另一種方法是將NewUser消息轉發到FetchImageActor。 然后,該actor檢查用戶是否具有avatar屬性集,如果是,則獲取圖像並將NewUserWithImage消息發送回MainActor,MainActor將此消息轉發給DoSomethingWithUserActor,然后DoSomethingWithUserActor實際上對包含的用戶對象和圖像執行某些操作數據。 我覺得這很糟糕,因為FetchImageActor需要有關用戶的知識,但它只是用於獲取圖像。 這是兩個不同的獨立方面,不應混合在一起。 在這種情況下, FetchImage消息對象也需要用戶屬性(我不喜歡如前所述)。

解決這個問題的正確或“好”策略是什么?

  1. 如果您需要互連actor,您可能會發現SynapseGrid庫很有用。

  2. 對於異步調用, Future是通常的方法。

     val gotImageFuture = new Future { fetchImageActor ? FetchImage(avatar) } gotImageFuture.onSuccess( (gotImage: GotImage) => doSomethingWithUserActor ! NewUserWithImage(newUser.user, gotImage.imageData) ) 
  3. 如果您添加相關令牌( User或至少是url )來獲取消息:

     GotImage(user:User, imageData: Array[Byte]) FetchImage(url:String, user:User) 

    那么你可能只是用火忘了。 主要角色將簡單地處理GotImage

     ... case GotImage(user, imageData) => doSomethingWithUserActor ! NewUserWithImage(user, imageData) 

    這種方法是暢通無阻的。

PS一個小建議:

case class NewUser(user: User, imageData:Option[Array[Byte]])

可以使處理更容易一些。

一種對您的用例非常有用的模式是使用ask-pipeTo。

您使用ask模式為您的請求獲取未來,然后(可選)對結果進行一些轉換(下面的示例結合了幾個響應,您也可以在將來調用map)並使用pipeTo模式發送結果未來的另一個演員。 這與Arseniy在第2點中的建議非常相似,事實上pipeTo在未來使用onSuccess進行注冊。

以下是優秀的akka文檔中的一個示例:

import akka.pattern.{ ask, pipe }
import system.dispatcher // The ExecutionContext that will be used
case class Result(x: Int, s: String, d: Double)
case object Request

implicit val timeout = Timeout(5 seconds) // needed for `?` below

val f: Future[Result] =
  for {
    x ← ask(actorA, Request).mapTo[Int] // call pattern directly
    s ← (actorB ask Request).mapTo[String] // call by implicit conversion
    d ← (actorC ? Request).mapTo[Double] // call by symbolic name
  } yield Result(x, s, d)

f pipeTo actorD // .. or ..
pipe(f) to actorD

我喜歡在這種情況下使用的另一種模式是使用臨時演員。 請記住,actor與線程不同,它們非常輕量級,創建它們不會帶來太多開銷。

你可以這樣做:

val tempActor = context.actorOf(Props(classOf[DoSomethingWithUserActor], newUser.user)
fetchImageActor.tell(FetchImage(image), tempActor)

這樣,DoSomethingActor已經具有對用戶的引用,並且通過將其設置為發送者,FetchImageActor可以只回復該消息,並且您的臨時actor將接收該圖像。 稍微玩一下這個想法,我覺得它在正確使用時非常強大。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM