如何在Akka中使用期货时确保消息的一致性

时间:2017-06-19 09:40:07

标签: scala asynchronous akka

我想了解当我在操作中进行异步调用时如何使用有状态的actor。

考虑以下演员:

@Singleton
class MyActor @Inject() () extends Actor with LazyLogging  {
  import context.dispatcher
  override def receive: Receive = {
    case Test(id: String) =>
      Future { logger.debug(s"id [$id]") }
    }
}

并调用此演员:

Stream.range(1, 100).foreach { i =>
  MyActor ! Test(i.toString)
}

这会给我一个不一致的系列印刷。

我如何在不失去整个“一条接一条消息”功能的情况下在演员中使用未来?

4 个答案:

答案 0 :(得分:2)

你应该将Future存储在var中,然后存储在你应该进行flatMap调用的每一条消息上。

if(storedFut == null) storedFut = Future { logger.debug(s"id [$id]") }
else storedFut = storedFut.flatMap(_ => Future { logger.debug(s"id [$id]") })

flatMap完全是订购Futures。

<强>旁注

如果你想让事情并行发生,你就会进入不确定区域,在那里你不能强制下令

答案 1 :(得分:2)

您所观察到的并不违反Akka的"message ordering per sender–receiver pair"保证;你正在观察的是Future s的不确定性,正如@almendar在他或她的回答中提到的那样。

Test消息按顺序发送到MyActor,从Test("1")发送到Test("100")MyActor 处理每个消息以相同顺序在其receive块中的消息。但是,您要在Future内记录每条消息,并且完成这些Futures的顺序是不确定的。这就是为什么你会看到系列打印不一致的原因。&#34;

要获得消息顺序记录的所需行为,请不要将记录包装在Future中。如果你必须在演员的Future区块中使用receive,那么@ almendar在演员中使用var的方法是安全的。

答案 2 :(得分:1)

您可以使用context.become和stash消息,等待将来的结束并处理另一条消息。 有关如何使用存储的更多信息,请参阅文档http://doc.akka.io/api/akka/current/akka/actor/Stash.html

中的示例

记住 - 只有在因网络特征而从同一台机器发送消息时才能保证消息排序。

答案 3 :(得分:-1)

另一种方法是在Future.onComplete上发送一条消息,假设对处理顺序没有限制

    //in receive  
    val future = Future { logger.debug(s"id [$id]") }
    f.onComplete {
        case Success(value) => self ! TestResult(s"Got the callback, meaning = $value")
        case Failure(e) => self ! TestError(e)
      }