우리는 각각의 액터들은 그들 자손 액터의 supervisor인 것을 알아보았다. 그리고 그러한 액터들은 supervisor strategy로 failure가 발생했을 시 fault handling 정책 2가지를 앞에서 정리했었다. 하지만 이러한 정책들은 수립되고 나면 바꾸지 못한다. 따라서 정책에 따른 fault tolerance를 정리할 예정이다.
이 섹션에서는 fault handling 매커니즘과 더 깊이가 깊어짐에 따라 사용할 수 있는 대안을 정리한 섹션이다.
만약 다음과 같은 OneForOneStrategy
를 사용한 코드가 있다고 보자.
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case _: NullPointerException => Restart
case _: IllegalArgumentException => Stop
case _: Exception => Escalate
}
위의 패턴매칭에서 우리가 잘 아는 예외 종류에 따라 어떤 행동을 할 지 분류해놓았다. 이때, akka.actor.SupervisorStrategy
의 OneForOneStrategy
를 이용해 선언되었다. 즉, 각 자손 액터들은 각자 다루어진다는 뜻이다. 위의 예시에서는 maxNrOfRetries
와 withinTimeRange
라는 파라미터 목록들을 넘겨받았다. 이들은 각각 10번(maxNrOfRetries 인자)까지 정책을 시도해보고 1분(withinTimeRange)이 지나면 재시작하라는 의미이다. 자속 액터는 maxNrOfRetries
횟수를 초과하거나 withinTimeRange
을 넘어가면 중지된다.
이때, 해당 파라미터들에 넣을 수 있는 특별한 값들을 알아보자.
maxNrOfRetries
의 -1, withinTimeRange
의 Duration.Inf
maxNrOfRetries
의 -1, withinTimeRange
의 유한한 값maxNrOfRetries
가 1로 취급maxNrOfRetries
가 양수, withinTimeRange
의 Duration.Inf
withinTimeRange
가 유한한 범위로 취급된다. 얼마나 걸리든, maxNrOfRetries
의 횟수를 초과하면 재시작된다.그리고 위의 예제 코드의 패턴 매칭 부분은 PartialFunction[Throwable, Directive]
인 Decider
타입이다. 이러한 단위는 자손 액터의 실패를 직접적으로 매칭해준다.
위의 예시에서 중요한점은, strategy는 supervising actor 내부에 선언되었다는 점이다. 이런 decider는 내부의 모든 상태로 접근이 가능하다. 스레드 세이프하게.
만약 정의된 정책 내부에 throw된 예외를 다루지 않는다면, Escalate
가 사용된다.
만약 supervisor strategy가 액터에 정의되어져 있지 않다면, 기본적으로 다음과 같이 다뤄진다.
ActorInitializationException
은 실패한 자손 액터를 멈춘다.ActorKilledException
은 실패한 자손 액터를 멈춘다.DeathPactException
은 실패한 자손 액터를 멈춘다.Exception
은 실패한 자손을 재시작시킨다.Throwable
의 다른 타입들은 부모 액터에 의해 escalate
되어진다.만약 예외 확대가 루트 가디언까지 올라간다면 이는 위의 5가지처럼 다루어진다.
이러한 점들을 고려하여 설계할 프로젝트의 정책을 위의 디폴트 정책과 고려하여 커스터마이즈 할 수 있다.
import akka.actor.OneForOneStrategy
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case _: ArithmeticException => Resume
case t =>
super.supervisorStrategy.decider.applyOrElse(t, (_: Any) => Escalate)
}
Erlang과 유사한 방식으로 자손 액터들을 멈추는 정책이 있다. SupervisorStrategy.stoppingStrategy
를 StoppingSupervisorStrategy
configurator와 함께 사용하면 된다. 이때, "/user" guardian에서 사용하면 된다.
Escalate 되지 않는 한 기본적으로 SupervisorStrategy
는 failure들을 로그로 기록한다. Escalated된 실패들은 핸들되어지기 때문에 잠재적으로 로그로 기록된므로, 계층 상에서 더 높은 곳에서 기록된다.
로그 레벨들 역시 Decider
에 의해 제공되며 컨트롤되어진다. SupervisorStrategy
의 LogLevel
로 적정한 결정 메서드를 제공하기 때문이다.
물론 로깅하는 것을 끌수도 있다. SupervisorStrategy
의 loggingEnabled
를 false로 바꾸어주면 된다. 그리고 로깅을 커스터마이징하는 것은 위에 나온것처럼 Decider
에서 제어하면 된다. 또한, logFailure
메소드를 오버라이드하여 커스터마이즈 가능하다.
저번 포스팅에서 정리했듯, system.actorOr()
를 사용하여 생성된 액터를 Toplevel actor라고 하며, 이 액터의 자손 액터를 User guardian
이라고 한다. 이러한 액터들에 대해서는 특별한 규칙은 존재하지 않으며, 구성된 정책을 따른다.