简体   繁体   English

如何使用Scala编写的Actor系统从未来获取数据?

[英]How to get data from a future in an Actor System written in Scala?

I have an Actor say ProcessTheCommand actor which has a receive method implemented as below: 我有一个演员说ProcessTheCommand演员,它具有如下实现的接收方法:

class ProcessTheCommand extends Actor {
   def receive = {
     case obj: DataObject => 
       val f1 = OneActor ? obj
       val f2 = SecondActor ? obj
       // val result= resultFromf1 + resultFromf2
       // do something using the result
   }
}

My problem: How do i get the data out of the two futures? 我的问题: 如何从两个期货中获取数据?

One way to do it is to use await but that is not the best practice. 一种方法是使用await但这不是最佳实践。 Suggestions? 有什么建议吗?

There are two parts to the question. 这个问题有两个部分。 The first is how to extract two futures and add them together, and the second one is how we process the result of these futures in Akka. 第一个是如何提取两个期货并将它们加在一起,第二个是我们如何在Akka中处理这些期货的结果。

Composing Futures: 组成期货:

There a couple of ways. 有几种方法。 I'm assuming the results of the futures is the same. 我假设期货的结果是相同的。 One would be using for comprehension: 一个会用于理解:

val firstFuture: Future[Int] = ???
val secondFuture: Future[Int] = ???
val result: Future[Int] = for {
  first <- firstFuture
  second <- secondFuture
} yield first + second

Another option would be to use Future.sequence : 另一种选择是使用Future.sequence

val firstFuture: Future[Int] = ???
val secondFuture: Future[Int] = ???

val result: Future[Int] = Future
  .sequence(Seq(firstFuture, secondFuture))
  .map {
    results => results.sum
  }

Another would be zip with map (thanks @ViktorKlang): 另一个将是带有map zip (感谢@ViktorKlang):

firstFuture
 .zip(secondFuture)
 .map { case (first, second) => first + second }

Or with Scala 2.12 zipWith : 或与Scala 2.12 zipWith

val result: Future[Int] = firstFuture.zipWith(secondFuture) {
  case (first, second) => first + second
}

Extracting the value inside the Actor: 在Actor中提取值:

The only missing piece is how we get the accumulated result. 唯一缺少的部分是我们如何获得累积结果。 The pattern in Akka is to pipe the result to your own Receive , since we never want to block on the method call, what we actually want is the future to invoke a callback once it completes, which is exactly what pipeTo will do. Akka中的模式是将结果通过管道传递到您自己的Receive ,因为我们从不希望在方法调用上阻塞,所以我们真正想要的是将来在完成回调后调用回调,这正是pipeTo会做的。

We'll create a custom case class which encapsulates the result: 我们将创建一个自定义案例类,其中封装了结果:

case class AccumulatedResult(result: Int)

And add it in Receive : 并将其添加到Receive

import akka.pattern.pipe
override def receive: Receive = {
  case obj: DataObject => 
    val firstFuture = OneActor ? obj
    val secondFuture = SecondActor ? obj
    firstFuture
     .zip(secondFuture)
     .map { case (first, second) => AccumulatedResult(first + second) }
     .pipeTo(self)

  case AccumulatedResult(res) => println(res)
}

The nice thing about this is that once the future completes the handling of the message will continue as part of the flow handling logic of the actor. 这样做的好处是,一旦将来完成,消息的处理将继续作为参与者的流处理逻辑的一部分。

Blocking inside receive method is a big NO, so never use await inside receive , otherwise you'll starve on resources. receive方法内部阻塞是一个很大的问题,因此不要在receive内部使用await,否则您将饿死资源。 All message processing happens in just a couple of worker threads. 所有消息处理仅在几个工作线程中进行。

To handle two futures, you might want to use their monadic properties: 要处理两个期货,您可能需要使用它们的单子属性:

val result: Future[Int] = firstFuture.flatMap { firstValue => 
   secondFuture.flatMap { secondValue => 
      firstValue + secondValue
   }
}

Or same thing via for-comprehension: 或通过理解理解相同的东西:

val result: Future[Int] = for {
  fistValue <- firstFuture
  secondValue <- secondFuture
} yield firstValue + secondValue

However, this is not what you usually want in actors, because you still get a Future and would need to block until it's completed. 但是,这不是演员通常需要的,因为您仍然可以获得Future,并且需要阻塞直到完成。 What you usually want is to pass the value to the caller or the next actor. 通常,您希望将值传递给调用方或下一个参与者。 And for this you can use pipe pattern: 为此,您可以使用pipe模式:

import akka.pattern.pipe
val resultFuture: Future[Int] = for {
  fistValue <- firstFuture
  secondValue <- secondFuture
} yield firstValue + secondValue
resultFuture pipeTo sender() // sends result to the sender when future is completed

See also: Futures in Akka 另请参阅: Akka期货

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM