我正在使用Spray应用程序中的ask模式调用Actor,并将结果作为HTTP响应返回。我将演员的失败映射到自定义错误代码。在发生故障时解决Akka期货问题
val authActor = context.actorOf(Props[AuthenticationActor])
callService((authActor ? TokenAuthenticationRequest(token)).mapTo[LoggedInUser]) { user =>
complete(StatusCodes.OK, user)
}
def callService[T](f: => Future[T])(cb: T => RequestContext => Unit) = {
onComplete(f) {
case Success(value: T) => cb(value)
case Failure(ex: ServiceException) => complete(ex.statusCode, ex.errorMessage)
case e => complete(StatusCodes.InternalServerError, "Unable to complete the request. Please try again later.")
//In reality this returns a custom error object.
}
}
这正常工作时authActor发送故障,但如果authActor抛出一个异常,没有任何反应,直到问超时完成。例如:
override def receive: Receive = {
case _ => throw new ServiceException(ErrorCodes.AuthenticationFailed, "No valid session was found for that token")
}
我知道阿卡文档说
不同,需要发送一个失败消息发送者例外完成未来。 这不会自动完成当一个actor在处理消息时抛出一个异常。
但是鉴于我使用了很多的喷射布线演员和服务演员之间的接口,我宁愿不用每个孩子演员的try/catch包装接收部分。是否有更好的方法来实现自动处理子actor中的异常,并在发生异常时立即解决未来?
编辑:这是我目前的解决方案。然而,为每个孩子演员做这件事都很麻烦。
override def receive: Receive = {
case default =>
try {
default match {
case _ => throw new ServiceException("")//Actual code would go here
}
}
catch {
case se: ServiceException =>
logger.error("Service error raised:", se)
sender ! Failure(se)
case ex: Exception =>
sender ! Failure(ex)
throw ex
}
}
这样,如果它是一个预期的错误(即ServiceException),它通过创建失败来处理。如果它是意外的,它会立即返回一个失败,以便未来得到解决,但是会抛出异常,因此它仍然可以由SupervisorStrategy处理。
那么......抛出异常之前发送失败消息。 –
这就是我不想做的 - 我说**我宁愿不用每个孩子演员的try/catch **包装接收部分。这是一个玩具的例子,我完全有可能不控制抛出异常的位置。 –
呃......你知道...... resilent分布式系统的基本路线之一就是“明确地制造错误”。想想可能发生的各种错误...让他们明确。如果你可以有“TypeSafe”错误...更好。 –