我想了解当我在操作中进行异步调用时如何使用有状态的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)
}
这会给我一个不一致的系列印刷。
我如何在不失去整个“一条接一条消息”功能的情况下在演员中使用未来?
答案 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)
}