Supervisor — 슈퍼바이저를 구현하는 behaviour 모듈

Supervisor — 슈퍼바이저를 구현하는 behaviour 모듈

Elixir/OTP에서 장애 허용(fault-tolerance) 의 중심에 서는 것이 바로 슈퍼바이저(Supervisor)예요. Supervisor는 슈퍼바이저를 구현하기 위한 behaviour 모듈이에요.

출처: Supervisor — A behaviour module for implementing supervisors

본문

슈퍼바이저는 다른 프로세스들을 감독하는 프로세스예요. 이때 감독받는 프로세스를 자식 프로세스(child processes) 라고 불러요. 슈퍼바이저들은 계층적인 프로세스 구조, 즉 감독 트리(supervision tree) 를 만드는 데 사용돼요. 감독 트리는 장애 허용성을 제공하고, 애플리케이션이 어떻게 시작되고 종료되는지를 캡슐화해요.

슈퍼바이저는 두 가지 방식으로 시작할 수 있어요.

  1. start_link/2를 통해 자식 스펙(child specifications)의 리스트를 직접 넘겨서 시작
  2. 필요한 콜백을 구현하는 모듈 기반(moduled-based) 슈퍼바이저를 정의해서 시작

대부분의 예제는 start_link/2로 슈퍼바이저를 시작하지만, 문서에는 모듈 기반 슈퍼바이저에 대한 별도 섹션도 있어요.

예제

슈퍼바이저를 시작하려면 먼저 감독할 자식 프로세스를 정의해야 해요. 예로 카운터를 유지하는 제네릭 서버인 GenServer를 정의해 볼게요. 다른 프로세스들은 이 프로세스에 메시지를 보내 카운터를 읽거나 값을 올릴 수 있어요.

defmodule Counter do
  use GenServer

  def start_link(arg) do
    GenServer.start_link(__MODULE__, arg)
  end
end

면책 조항(Disclaimer): 실제로 카운터를 GenServer로 정의할 일은 없어요. 카운터가 필요하면 그것을 함수의 입력·출력으로 그냥 넘겨 다니면 되거든요. 이 예제에서 카운터를 고른 이유는 단순해서 슈퍼바이저가 어떻게 동작하는지에 집중할 수 있기 때문이에요.

감독 트리와 장애 처리

감독 트리는 위에서 아래로 구성돼요. 최상위에 최상위 슈퍼바이저가 있고, 그 아래에 자식으로 워커(worker)나 또 다른 슈퍼바이저가 붙어요. 자식이 비정상적으로 종료되면 슈퍼바이저는 설정된 재시작 전략(strategy) 에 따라 해당 자식을 다시 시작해요. 이렇게 하면 단일 프로세스가 죽더라도 시스템 전체가 죽지 않고 회복될 수 있어요.

슈퍼바이저가 자식을 다시 시작하는 방식은 :one_for_one, :one_for_all, :rest_for_one, :simple_one_for_one 같은 strategy로 정해요. 어떤 자식이 죽었을 때 그 자식만 다시 시작할지, 형제들까지 전부 재시작할지가 전략에 따라 달라져요.

더 알아보기

  • 시작: Supervisor.start_link/2, Supervisor.start_link/3
  • 콜백: Supervisor.init/1 (모듈 기반 슈퍼바이저)
  • 관리: Supervisor.terminate_child/2, Supervisor.restart_child/2, Supervisor.count_children/1, Supervisor.which_children/1
  • 관련 모듈: Supervisor.Spec (자식 스펙, 단 deprecated), DynamicSupervisor, Application
  • 가이드: supervisor-and-application (Registries and supervision trees)