0Pricing
Scala for Backend Engineering & Functional Programming · 강의

감독과 내결함성

장애에서 안정적으로 복구할 수 있는 회복력 있는 시스템을 만들기 위해 Akka의 감독 전략을 살펴봅니다.

감독과 내결함성은(는) CoddyKit의 무료 Scala for Backend Engineering & Functional Programming 강의입니다. 이것은 3개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Scala for Backend Engineering & Functional Programming 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Scala for Backend Engineering & Functional Programming 강의에는 총 3개의 강의가 포함되어 있습니다.

복원력 있는 시스템 구축하기

동시성 애플리케이션에서는 여러 문제가 발생할 수 있습니다. 액터가 충돌하거나 네트워크 호출이 실패하거나 데이터베이스에 연결할 수 없을 수도 있습니다.

장애 허용이란 시스템의 일부가 실패하더라도 시스템이 올바르게 계속 작동하는 능력입니다. Akka는 이를 실현하기 위한 강력한 메커니즘을 제공합니다.

부모-자식 감독

Akka 액터는 가계도와 비슷한 계층 구조로 구성됩니다. 한 액터가 다른 액터를 생성하면 생성한 액터가 다른 액터의 부모가 됩니다.

  • 부모는 자식을 감독할 책임이 있습니다.
  • 자식 액터가 실패하면 부모는 알림을 받습니다.
  • 그런 다음 부모가 자식의 실패를 처리하는 방법을 결정합니다. 이를 감독 전략이라고 합니다.

실패 처리 결정하기

액터가 실패하면 부모는 다음 네 가지 지시 중 하나를 내릴 수 있습니다.

  • Resume: 실패를 무시하고 액터가 메시지 처리를 계속합니다.
  • Restart: 액터를 중지하고 다시 생성한 후 처리를 재개합니다. 내부 상태는 초기화됩니다.
  • Stop: 액터를 영구적으로 종료합니다.
  • Escalate: 실패를 부모의 감독자(조부모)에게 전달합니다.

OneForOne 전략

OneForOneStrategy는 일반적으로 사용되는 감독 전략입니다. 이 전략에서는 실패한 자식 액터에만 지시가 적용됩니다.

예를 들어 자식 액터 하나가 충돌하면 부모는 해당 자식만 재시작하고 다른 자식에는 영향을 주지 않을 수 있습니다.

OneForOne 실제 적용

OneForOneStrategy를 살펴보겠습니다. ArithmeticException이 발생하면 자식을 재시작하고 다른 예외가 발생하면 자식을 중지하는 부모를 생성합니다.

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object OneForOneExample extends App {
  class Child extends Actor {
    override def preStart(): Unit = {
      println(s"Child ${self.path.name}: Started!")
    }
    override def postStop(): Unit = {
      println(s"Child ${self.path.name}: Stopped!")
    }
    override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
      println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
    }
    override def postRestart(reason: Throwable): Unit = {
      println(s"Child ${self.path.name}: Post-restart!")
    }
    def receive = {
      case "divideByZero" => throw new ArithmeticException("Oops, dividing by zero!")
      case "nullPointer"  => throw new NullPointerException("Something is null!")
      case msg            => println(s"Child ${self.path.name}: Received $msg")
    }
  }

  class Parent extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
        case _: ArithmeticException => Restart
        case _: NullPointerException => Stop
        case _                      => Escalate
      }

    val child = context.actorOf(Props[Child], "myChild")

    def receive = {
      case msg => child ! msg
    }
  }

  val system = ActorSystem("OneForOneSystem")
  val parent = system.actorOf(Props[Parent], "myParent")

  parent ! "Hello"
  parent ! "divideByZero" // Should cause a restart
  Thread.sleep(100)
  parent ! "Hello again"
  Thread.sleep(1000) // Give time for restart to complete

  parent ! "nullPointer"  // Should cause a stop
  Thread.sleep(100)
  parent ! "Hello after null" // This message will not be processed by child
  Thread.sleep(2000)

  system.terminate()
}

OneForOne 동작 확인

이전 코드를 실행해 보셨나요? 다음과 같은 일이 발생했습니다.

  • "divideByZero"가 전송되자 자식에서 ArithmeticException이 발생했습니다. 부모는 Restart를 적용했습니다. "Pre-restart"와 "Post-restart" 로그가 표시되었습니다.
  • 그다음 자식은 "Hello again"을 처리했습니다.
  • "nullPointer"가 전송되자 자식에서 NullPointerException이 발생했습니다. 부모는 Stop을 적용했습니다. 자식은 종료되었습니다.
  • "Hello after null"이 전송되었지만 자식은 이미 중지되어 처리할 수 없었습니다.

AllForOne 전략

때로는 한 자식의 실패로 인해 모든 형제 자식도 문제가 생기거나 정상적으로 작동하지 못할 수 있습니다.

AllForOneStrategy는 실패한 자식 하나에만 적용하는 것이 아니라 감독자의 모든 자식에게 동일한 지시를 적용합니다. 한 자식이 실패하면 선택한 지시에 따라 모든 자식이 영향을 받습니다.

AllForOne 실제 적용

부모가 AllForOneStrategy를 사용하도록 수정해 보겠습니다. 한 자식의 실패가 형제 자식에게 어떤 영향을 미치는지 확인해 보세요.

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object AllForOneExample extends App {
  class Child extends Actor {
    override def preStart(): Unit = {
      println(s"Child ${self.path.name}: Started!")
    }
    override def postStop(): Unit = {
      println(s"Child ${self.path.name}: Stopped!")
    }
    override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
      println(s"Child ${self.path.name}: Pre-restart due to ${reason.getMessage}")
    }
    override def postRestart(reason: Throwable): Unit = {
      println(s"Child ${self.path.name}: Post-restart!")
    }
    def receive = {
      case "fail" => throw new RuntimeException("Child failed!")
      case msg    => println(s"Child ${self.path.name}: Received $msg")
    }
  }

  class Parent extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      AllForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1.minute) {
        case _: RuntimeException => Restart
        case _                   => Escalate
      }

    val childA = context.actorOf(Props[Child], "childA")
    val childB = context.actorOf(Props[Child], "childB")

    def receive = {
      case "failA" => childA ! "fail"
      case "msgB"  => childB ! "Hello from B"
      case msg     => println(s"Parent received: $msg")
    }
  }

  val system = ActorSystem("AllForOneSystem")
  val parent = system.actorOf(Props[Parent], "myParent")

  parent ! "msgB" // Child B receives a message
  Thread.sleep(100)

  parent ! "failA" // Child A fails, causing all children to restart
  Thread.sleep(1000)

  parent ! "msgB" // Child B (restarted) receives another message
  Thread.sleep(2000)

  system.terminate()
}

판정 함수 사용자 지정하기

판정 함수를 사용하여 매우 세밀하게 사용자 지정한 감독 로직을 만들 수 있습니다. 이 함수는 Throwable(예외)을 받아 Directive를 반환합니다.

이를 통해 예외의 유형, 예외를 발생시킨 메시지 또는 감독자의 상태에 따라 복잡한 규칙을 구현할 수 있습니다.

고급 감독

세밀하게 제어할 수 있도록 감독 전략 안에서 사용자 지정 판정 함수를 정의하는 방법은 다음과 같습니다.

import akka.actor._
import akka.actor.SupervisorStrategy._
import scala.concurrent.duration._

object CustomDeciderExample extends App {
  class Worker extends Actor {
    def receive = {
      case "criticalError" => throw new IllegalStateException("Critical state!")
      case "minorError"    => throw new IllegalArgumentException("Bad input!")
      case msg             => println(s"Worker: Received $msg")
    }
  }

  class Supervisor extends Actor {
    override val supervisorStrategy: SupervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 3, withinTimeRange = 10.seconds) {
        case _: IllegalArgumentException => Resume // Minor error, just resume
        case _: IllegalStateException    => Restart // Critical, restart
        case _: Exception                => Stop    // Unknown, stop
        case _                           => Escalate // Others, escalate
      }

    val worker = context.actorOf(Props[Worker], "myWorker")

    def receive = {
      case msg => worker ! msg
    }
  }

  val system = ActorSystem("CustomDeciderSystem")
  val supervisor = system.actorOf(Props[Supervisor], "mySupervisor")

  supervisor ! "Hello"
  supervisor ! "minorError" // Should Resume
  Thread.sleep(100)
  supervisor ! "Hello again" // Worker should still be active
  Thread.sleep(500)

  supervisor ! "criticalError" // Should Restart
  Thread.sleep(100)
  supervisor ! "After critical" // Worker should be restarted
  Thread.sleep(1500)

  system.terminate()
}

지식 확인하기

childA와 childB라는 두 자식 액터를 감독하는 부모 액터를 생각해 보세요. 부모는 모든 예외에 대해 Restart 지시를 사용하는 AllForOneStrategy를 적용합니다.

childA에서 예외가 발생하면 어떤 일이 일어날까요?

복습: 장애 허용

잘하셨습니다! 이번 레슨에서는 Akka의 강력한 감독 메커니즘을 배웠습니다.

  • 감독 계층: 부모가 자식을 감독합니다.
  • 지시: Resume, Restart, Stop, Escalate
  • OneForOneStrategy: 실패한 자식에게만 지시를 적용합니다.
  • AllForOneStrategy: 모든 자식에게 지시를 적용합니다.
  • 사용자 지정 판정 함수: 실패 처리를 세밀하게 제어할 때 사용합니다.

이러한 도구는 Akka 액터를 사용하여 견고하고 복원력 있는 동시성 애플리케이션을 구축하는 데 필수적입니다!

자주 묻는 질문

“감독과 내결함성” 강의는 무료인가요?

네 — “감독과 내결함성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Scala for Backend Engineering & Functional Programming 강의 전체를 잠금 해제할 수 있습니다. Scala for Backend Engineering & Functional Programming 강의에는 총 3개의 강의가 포함되어 있습니다.

“감독과 내결함성”에서 뭘 배우나요?

장애에서 안정적으로 복구할 수 있는 회복력 있는 시스템을 만들기 위해 Akka의 감독 전략을 살펴봅니다. 브라우저에서 직접 실행하는 실습 코드로 Scala for Backend Engineering & Functional Programming을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Scala for Backend Engineering & Functional Programming을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Scala for Backend Engineering & Functional Programming은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 3개 중 3번째 강의입니다.

“감독과 내결함성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Scala for Backend Engineering & Functional Programming 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Scala for Backend Engineering & Functional Programming 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. Akka 액터 모델 기초
  2. 액터 시스템 설계
  3. 감독과 내결함성
← Scala for Backend Engineering & Functional Programming(으)로 돌아가기