[英]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
處理消息NewUser
和NewUserWithImage
。 也許是這樣的:
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
消息對象也需要用戶屬性(我不喜歡如前所述)。
解決這個問題的正確或“好”策略是什么?
如果您需要互連actor,您可能會發現SynapseGrid庫很有用。
對於異步調用, Future
是通常的方法。
val gotImageFuture = new Future { fetchImageActor ? FetchImage(avatar) } gotImageFuture.onSuccess( (gotImage: GotImage) => doSomethingWithUserActor ! NewUserWithImage(newUser.user, gotImage.imageData) )
如果您添加相關令牌( 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.