gpt4 book ai didi

scala - 在Akka actor中累积状态的正确模式

转载 作者:行者123 更新时间:2023-12-04 03:07:42 24 4
gpt4 key购买 nike

问题:

在Akka Actor 中累积状态的正确模式是什么?

上下文:

假设我有一些服务都返回数据。

class ServiceA extends Actor {
def receive = {
case _ => sender ! AResponse(100)
}
}

class ServiceB extends Actor {
def receive = {
case _ => sender ! BResponse("n")
}
}

// ...

我希望有一个控制/监督参与者,负责协调与所有这些服务的对话并跟踪其响应,然后将包含所有数据的响应发送回原始发送者。
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会随着接收到的不同消息而不断变化,当我等待 var消息时,我不可能指望该 BResponse停留。

我意识到我可以使用 ask,也可以只使用nest/flatMap Future,但是据我所读,这是一个错误的模式。没有Future,有没有办法实现所有这些目标?

最佳答案

由于从不同时从多个线程访问actor,因此您可以轻松地在所需的状态中存储和更改任何状态。例如,您可以这样做:

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()机制完成的。
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的接收函数以反射(reflect)该新状态。

请注意,上面的代码非常简单,当可能有很多“原始发件人”时,它将无法正常工作。在这种情况下,您必须为所有消息添加一个ID,并使用它们来确定哪些响应属于哪个“原始发件人”状态,或者您可以创建多个参与者,每个“原始发件人”中的每个参与者。

关于scala - 在Akka actor中累积状态的正确模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24708096/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com