Sto chiamando un attore che utilizza il modello ask all'interno di un'applicazione Spray e restituisce il risultato come risposta HTTP. Mappo i fallimenti dall'attore a un codice di errore personalizzato.Risoluzione del futuro Akka da chiedere in caso di guasto
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.
}
}
questo funziona correttamente quando l'authActor invia un fallimento, ma se l'authActor genera un'eccezione, non succede nulla fino a quando il timeout chiedere completa. Per esempio:
override def receive: Receive = {
case _ => throw new ServiceException(ErrorCodes.AuthenticationFailed, "No valid session was found for that token")
}
So che i documenti dicono che Akka
Per completare il futuro con un'eccezione è necessario inviare un messaggio di mancata al mittente. Questo non viene eseguito automaticamente quando un attore genera un'eccezione durante l'elaborazione di un messaggio.
ma dato che io uso richiede un sacco di interfaccia tra gli attori di routing Spray e gli attori di servizio, preferirei non avvolgere la parte di ricezione di ogni attore bambino con un try/catch. Esiste un modo migliore per ottenere la gestione automatica delle eccezioni negli attori figli e risolvere immediatamente il futuro in caso di eccezione?
Modifica: questa è la mia soluzione corrente. Tuttavia, è abbastanza complicato farlo per ogni attore bambino.
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
}
}
In questo modo se si tratta di un errore previsto (cioè ServiceException), è gestita creando un guasto. Se è inatteso, restituisce immediatamente un errore in modo che il futuro venga risolto, ma genera l'eccezione in modo che possa essere gestito da SupervisorStrategy.
Bene ... inviare un messaggio di errore prima di lanciare l'eccezione. –
Questo è quello che non volevo fare - ho detto ** Preferirei non avvolgere la parte di ricezione di ogni attore bambino con un try/catch **. Questo è un esempio di giocattolo, è perfettamente possibile che io non controlli dove viene lanciata l'eccezione. –
Beh ... sai ... una delle strade fondamentali dei sistemi distribuiti resilienti è quella di "rendere gli errori espliciti". Pensa ai vari errori che possono accadere ... rendili espliciti. Se puoi avere errori "TypeSafe" ... ancora meglio. –