Я звоню актеру, используя шаблон запроса в приложении Spray и возвращаю результат в качестве ответа 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")
}
Я знаю, что документы Akka говорят, что
Чтобы завершить будущее с исключением вам нужно послать сообщение об ошибке отправителю. Это не делается автоматически, когда актер выдает исключение при обработке сообщения.
Но, учитывая, что я использую много интерфейсов между участниками маршрутизации спрей и участниками службы, я бы предпочел не обертывать принимающую часть каждого дочернего актера с помощью try/catch. Есть ли лучший способ добиться автоматической обработки исключений в дочерних актерах и немедленно решить будущее в случае исключения?
Редактировать: это мое текущее решение. Однако, это довольно грязно, чтобы сделать это для каждого дочернего актера.
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 **. Это игрушечный пример, вполне возможно, что я не контролирую, где генерируется исключение. –
Ну ... вы знаете ... одна из фундаментальных дорог устойчивых распределенных систем - «делать ошибки явными». Подумайте о различных ошибках, которые могут произойти ... сделайте их явными. Если у вас могут быть ошибки «TypeSafe» ... еще лучше. –