示例
import akka.actor.typed.scaladsl.AskPattern._
object MyActor {
sealed trait Command
case class MyCommand(replyTo: ActorRef[Try[String]]) extends Command
def apply(): Behaviors.Receive[Command] = {
val failureMessage = util.Failure(new RuntimeException("Timeout"))
Behaviors.receiveMessage {
case MyCommand(replyTo) => replyTo ! failureMessage
Behaviors.same
}
}
}
val worker = testKit.spawn(MyActor())
// Act
val res = worker.ask[Try[String]](me => MyActor.MyCommand(replyTo = me))
// Assert
println(Await.result(res, Duration.Inf)) // Return Failure(java.lang.RuntimeException: Timeout)
// But i want to do:
intercept[RuntimeException] {
Await.result(res, Duration.Inf)
}
// and on success i want to return instead of Success[T] as T
}
请注意,我使用Try
包装结果,以便返回Success
或Failure
。问题是我不想这么做,我想在ask
请求上使整个Future
失败。
有可能做到吗?
对于询问响应是成功响应还是失败响应,在Akka Typed中,最好使用askWithStatus
并调整协议以使用StatusReply
:
import akka.pattern.StatusReply
object MyActor {
sealed trait Command
case class MyCommand(replyTo: ActorRef[StatusReply[String]]) extends Command
// can also wrap a Throwable, but it's generally advised to wrap a string
val failure = StatusReply.Error("Timeout")
def apply(): Behavior[Command] =
Behaviors.receiveMessage {
case MyCommand(replyTo) =>
replyTo ! failure
Behaviors.same
}
}
然后,执行请求:
val res: Future[String] = worker.askWithStatus[String](MyActor.MyCommand(_))
res
将:
- 如果被询问的演员回答
StatusReply.Success(string)
,则用string
完成 - 如果被询问的参与者回答为
StatusReply.Error(errorString)
,则以StatusReply.ErrorMessage(errorString)
失败 - 如果被询问的参与者回答为
StatusReply.Error(x: Throwable)
,则以x
失败 - 如果在收到响应之前达到超时,则正常
ask
将失败
至于为什么建议包装String错误消息,这是因为通常情况下,请求参与者执行某些操作的代码不应该对异常消息之外的任何异常部分的参与者内部有足够的了解,并且(可能(异常的类型根本没有意义。如果您不愿意向web或API消费者公开该细节(例如堆栈跟踪(,那么您可能不应该在询问响应中公开该细节。如果你在餐馆点了菜,厨房的厨师把锅和你的食物掉在地板上,你需要服务员(你去厨房的API(回来告诉你";厨师把你的食物掉在地板上了;还是服务员或经理过来说";对不起,您的订单会延迟,这里有一杯饮料(或者礼券什么的("?