[Akka] classic fault tolerance 1

smlee·2023년 8월 24일
0

Akka

목록 보기
8/50
post-thumbnail

우리는 각각의 액터들은 그들 자손 액터의 supervisor인 것을 알아보았다. 그리고 그러한 액터들은 supervisor strategy로 failure가 발생했을 시 fault handling 정책 2가지를 앞에서 정리했었다. 하지만 이러한 정책들은 수립되고 나면 바꾸지 못한다. 따라서 정책에 따른 fault tolerance를 정리할 예정이다.


Creating a Supervisor Strategy

이 섹션에서는 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.SupervisorStrategyOneForOneStrategy를 이용해 선언되었다. 즉, 각 자손 액터들은 각자 다루어진다는 뜻이다. 위의 예시에서는 maxNrOfRetrieswithinTimeRange라는 파라미터 목록들을 넘겨받았다. 이들은 각각 10번(maxNrOfRetries 인자)까지 정책을 시도해보고 1분(withinTimeRange)이 지나면 재시작하라는 의미이다. 자속 액터는 maxNrOfRetries 횟수를 초과하거나 withinTimeRange을 넘어가면 중지된다.

이때, 해당 파라미터들에 넣을 수 있는 특별한 값들을 알아보자.

  1. maxNrOfRetries의 -1, withinTimeRangeDuration.Inf
    • 자손이 제한 없이 항상 재시작된다.
  2. maxNrOfRetries의 -1, withinTimeRange의 유한한 값
    • maxNrOfRetries가 1로 취급
  3. maxNrOfRetries가 양수, withinTimeRangeDuration.Inf
    • withinTimeRange가 유한한 범위로 취급된다. 얼마나 걸리든, maxNrOfRetries의 횟수를 초과하면 재시작된다.

그리고 위의 예제 코드의 패턴 매칭 부분은 PartialFunction[Throwable, Directive]Decider 타입이다. 이러한 단위는 자손 액터의 실패를 직접적으로 매칭해준다.

위의 예시에서 중요한점은, strategy는 supervising actor 내부에 선언되었다는 점이다. 이런 decider는 내부의 모든 상태로 접근이 가능하다. 스레드 세이프하게.

Default supervisor Strategy

만약 정의된 정책 내부에 throw된 예외를 다루지 않는다면, Escalate가 사용된다.

만약 supervisor strategy가 액터에 정의되어져 있지 않다면, 기본적으로 다음과 같이 다뤄진다.

  1. ActorInitializationException은 실패한 자손 액터를 멈춘다.
  2. ActorKilledException은 실패한 자손 액터를 멈춘다.
  3. DeathPactException은 실패한 자손 액터를 멈춘다.
  4. Exception은 실패한 자손을 재시작시킨다.
  5. 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)
  }

Supervisor Strategy 멈추기

Erlang과 유사한 방식으로 자손 액터들을 멈추는 정책이 있다. SupervisorStrategy.stoppingStrategyStoppingSupervisorStrategy configurator와 함께 사용하면 된다. 이때, "/user" guardian에서 사용하면 된다.

Actor Failure 로깅

Escalate 되지 않는 한 기본적으로 SupervisorStrategy는 failure들을 로그로 기록한다. Escalated된 실패들은 핸들되어지기 때문에 잠재적으로 로그로 기록된므로, 계층 상에서 더 높은 곳에서 기록된다.

로그 레벨들 역시 Decider에 의해 제공되며 컨트롤되어진다. SupervisorStrategyLogLevel로 적정한 결정 메서드를 제공하기 때문이다.

물론 로깅하는 것을 끌수도 있다. SupervisorStrategyloggingEnabled를 false로 바꾸어주면 된다. 그리고 로깅을 커스터마이징하는 것은 위에 나온것처럼 Decider에서 제어하면 된다. 또한, logFailure 메소드를 오버라이드하여 커스터마이즈 가능하다.

Top-level actors

저번 포스팅에서 정리했듯, system.actorOr()를 사용하여 생성된 액터를 Toplevel actor라고 하며, 이 액터의 자손 액터를 User guardian이라고 한다. 이러한 액터들에 대해서는 특별한 규칙은 존재하지 않으며, 구성된 정책을 따른다.

Reference

0개의 댓글