我正在使用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」錯誤...更好。 –