首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >阿克卡演员状态累积的正确模式

阿克卡演员状态累积的正确模式
EN

Stack Overflow用户
提问于 2014-07-11 23:36:54
回答 2查看 6.6K关注 0票数 10

问题:

在阿克卡演员身上积累状态的正确模式是什么?

上下文:

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

代码语言:javascript
复制
class ServiceA extends Actor {
  def receive = {
    case _ => sender ! AResponse(100)
  }
}

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

// ...

我希望有一个控制/监督参与者来协调与所有这些服务的对话,并跟踪他们的响应,然后用所有数据向原始发件人发送一个响应。

代码语言:javascript
复制
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

我意识到我可以使用ask和nest/flatMap Future,但从我所读到的来看,这是一个糟糕的模式。有没有办法在没有未来的情况下实现这一切?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-12 09:50:51

因为参与者从来不会同时从多个线程访问,所以您可以轻松地存储和更改它们中的任何状态。例如,您可以这样做:

代码语言:javascript
复制
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()机制完成的。

代码语言:javascript
复制
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方法的参数中,当需要更新此状态时,只需切换参与者的接收函数来反映这种新状态。

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

票数 17
EN

Stack Overflow用户

发布于 2014-07-14 04:08:18

我相信Akka的方式是根据请求使用演员模式。这样,当您每次收到请求时,您可以找出对应于什么的响应,从而创建一个新的参与者。这很便宜,事实上,每次你问()时都会发生。

这些请求处理器(我就是这样称呼它们)通常有简单的响应字段。这仅仅是一个简单的空比较的问题,看看请求是否已经到达。

使用此方案,重试/失败也会容易得多。暂停也是。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24708096

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档