问题描述
问题:
在Akka演员中累积状态的正确模式是什么?
What is the correct pattern for accumulating state in an Akka actor?
上下文:
假设我有一些服务都返回了数据。
Let's say I have a few services that all return data.
class ServiceA extends Actor {
def receive = {
case _ => sender ! AResponse(100)
}
}
class ServiceB extends Actor {
def receive = {
case _ => sender ! BResponse("n")
}
}
// ...
我想要一个控制/监督参与者,协调与所有这些服务的对话并跟踪其响应,然后将所有数据的响应发送回原始发送者。
I want to have one controlling/supervising actor that coordinates talking to all these services and keeping track of their responses, then sending a response with all the data back to the original sender.
class Supervisor extends Actor {
def receive = {
case "begin" => begin
case AResponse(id) => ???
case BResponse(letter) => ???
}
// end goal:
def gotEverything(id: Int, letter: String) =
originalSender ! (id, letter)
def begin = {
ServiceA ! "a"
ServiceB ! "b"
}
}
随着服务响应的到来,怎么办我将所有状态保持在一起吗?据我了解,如果我将AResponse的值分配给 var aResponse:Int
,则var会不断变化,因为接收到不同的消息,而没有我可能会指望等待 BResponse
消息时留下的 var
。
As service responses come in, how do I keep all of that state associated together? As I understand it, if I was to assign the value of AResponse to, say, var aResponse: Int
, that var is constantly changing as different messages are being received and it's not possible for me to count on that var
staying while I wait for the BResponse
message.
我意识到我可以使用 ask
并只嵌套/ flatMap Future
,但是从我阅读的内容来看,这是一个糟糕的模式。
I realize I could use ask
and just nest/flatMap Future
's, but from what I've read that is a bad pattern. Is there a way to achieve all this without Future's?
推荐答案
因为从不同时从多个线程访问actor,所以可以轻松存储并更改您想要的任何状态。例如,您可以执行以下操作:
Because actors are never accessed from multiple threads simultaneously, you can easily store and mutate any state in them you want. For example, you can do this:
class Supervisor extends Actor {
private var originalSender: Option[ActorRef] = None
private var id: Option[WhateverId] = None
private var letter: Option[WhateverLetter] = None
def everythingReceived = id.isDefined && letter.isDefined
def receive = {
case "begin" =>
this.originalSender = Some(sender)
begin()
case AResponse(id) =>
this.id = Some(id)
if (everythingReceived) gotEverything()
case BResponse(letter) =>
this.letter = Some(letter)
if (everythingReceived) gotEverything()
}
// end goal:
def gotEverything(): Unit = {
originalSender.foreach(_ ! (id.get, letter.get))
originalSender = None
id = None
letter = None
}
def begin(): Unit = {
ServiceA ! "a"
ServiceB ! "b"
}
}
不过,还有更好的方法。您可以使用没有显式状态变量的参与者来模拟某种状态机。这是通过 become()
机制完成的。
There is a better way, however. You can emulate some kind of state machine with actors without explicit state variables. This is done using become()
mechanism.
class Supervisor extends Actor {
def receive = empty
def empty: Receive = {
case "begin" =>
AService ! "a"
BService ! "b"
context become noResponses(sender)
}
def noResponses(originalSender: ActorRef): Receive = {
case AResponse(id) => context become receivedId(originalSender, id)
case BResponse(letter) => context become receivedLetter(originalSender, letter)
}
def receivedId(originalSender: ActorRef, id: WhateverId): Receive = {
case AResponse(id) => context become receivedId(originalSender, id)
case BResponse(letter) => gotEverything(originalSender, id, letter)
}
def receivedLetter(originalSender: ActorRef, letter: WhateverLetter): Receive = {
case AResponse(id) => gotEverything(originalSender, id, letter)
case BResponse(letter) => context become receivedLetter(originalSender, letter)
}
// end goal:
def gotEverything(originalSender: ActorRef, id: Int, letter: String): Unit = {
originalSender ! (id, letter)
context become empty
}
}
这可能会有些冗长,但不包含显式变量;所有状态都隐式包含在 Receive
方法的参数中,并且当需要更新此状态时,仅切换actor的接收函数以反映该新状态。
This may be slightly more verbose, but it does not contain explicit variables; all state is implicitly contained in parameters of Receive
methods, and when this state needs to be updated, actor's receive function is just switched to reflect this new state.
请注意,上面的代码非常简单,当可以有很多原始发件人时,它将无法正常工作。在这种情况下,您必须为所有邮件添加一个ID,并使用它们来确定哪些响应属于哪个原始发件人状态,或者您可以为每个原始发件人创建多个参与者。
Note that the above code is very simple and it won't work properly when there can be many "original senders". In that case you'll have to add an id to all messages and use them to determine which responses belong to which "original sender" state or you can create multiple actors, each for every one of the "original senders".
这篇关于在Akka actor中累积状态的正确模式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!