对有类型的 Actor 提出请求失败,而不是用 Try 换行
Fail Ask Request on Typed Actor instead wrap with Try
示例
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
包装结果以便 return 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
如果在收到响应之前达到超时,则会失败
至于为什么建议包装一个字符串错误消息,这是因为通常情况下,请求 actor 做某事的代码不应该对 actor 的任何部分的内部结构有足够的了解异常消息和(可能)异常类型之外的异常是有意义的。如果您不愿意将该细节(例如堆栈跟踪)暴露给网络或 API 消费者,您可能不应该在您的询问响应中暴露它。如果您在餐厅点餐,而厨房里的厨师将装有您食物的平底锅掉在地上,您是否需要服务员(您 API 到厨房)回来告诉您“厨师将您的食物掉在了地上”地板上的食物”,或者只是让服务员或经理过来说“抱歉,您的订单会延迟,这里有一杯饮料(或礼券或其他)”?
示例
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
包装结果以便 return 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)
,则用 - 如果被询问的演员回答
StatusReply.Error(errorString)
,则失败 - 如果被询问的演员回答
StatusReply.Error(x: Throwable)
,则失败 - 失败,通常的异常是正常的
ask
如果在收到响应之前达到超时,则会失败
string
完成
StatusReply.ErrorMessage(errorString)
x
至于为什么建议包装一个字符串错误消息,这是因为通常情况下,请求 actor 做某事的代码不应该对 actor 的任何部分的内部结构有足够的了解异常消息和(可能)异常类型之外的异常是有意义的。如果您不愿意将该细节(例如堆栈跟踪)暴露给网络或 API 消费者,您可能不应该在您的询问响应中暴露它。如果您在餐厅点餐,而厨房里的厨师将装有您食物的平底锅掉在地上,您是否需要服务员(您 API 到厨房)回来告诉您“厨师将您的食物掉在了地上”地板上的食物”,或者只是让服务员或经理过来说“抱歉,您的订单会延迟,这里有一杯饮料(或礼券或其他)”?