Fork me on GitHub

Nadzorcy OTP

Nadzorcy to wyspecjalizowane procesy mające tylko jeden cel: monitorowanie innych procesów. Pozwalają oni na tworzenie aplikacji odpornych na błędy, które będą samodzielnie restartować procesy, które zawiodły.

Spis treści

Konfiguracja

Cała „magia” nadzorców dzieje się w funkcji Supervisor.start_link/2. Poza uruchomieniem nadzorcy i procesów potomnych pozwala ona na określenie strategii użytej do zarządzania procesami potomnymi.

Procesy potomne są przekazywane jako lista do funkcji worker/3, zaimportowanej z Supervisor.Spec. Funkcja worker/3 jako parametry przyjmuje moduł, argumenty wywołania oraz opcje. W praktyce funkcja worker/3 wywołuje start_link/3 przekazując do niej podane przez nasz argumenty.

Zmodyfikujmy przykład SimpleQueue z lekcji Współbieżność OTP:

import Supervisor.Spec

children = [
  worker(SimpleQueue, [], [name: SimpleQueue])
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Jeżeli nadzorowany proces ulegnie awarii albo się zakończy, nadzorca automatycznie go zrestartuje, tak jak by nic się nie stało.

Strategie

Do zarządzania procesami potomnymi nadzorca może wykorzystać jedną z czterech strategii:

Restart procesu potomnego

Restart procesu potomnego można obsłużyć na kilka sposobów:

Konfiguracja ta jest opcjonalna, a wartością domyślną jest :permanent.

Zagnieżdżanie

Poza procesami potomnymi możemy też tworzyć nadzorców, którzy będą zarządzać innymi nadzorcami. W ten sposób tworzymy drzewo nadzorców. Jedyna różnica polega na użyciu funkcji supervisor/3 zamiast worker/3:

import Supervisor.Spec

children = [
  supervisor(ExampleApp.ConnectionSupervisor, [[name: ExampleApp.ConnectionSupervisor]]),
  worker(SimpleQueue, [[], [name: SimpleQueue]])
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Nadzorcy zadań

Zadania mają swojego wyspecjalizowanego nadzorcę Task.Supervisor. Zaprojektowany jest on z myślą o dynamicznym tworzeniu zadań, oznacza to, że używa strategii :simple_one_for_one.

Przygotowanie

Użycie Task.Supervisor nie różni się od użycia innych nadzorców:

import Supervisor.Spec

children = [
  supervisor(Task.Supervisor, [[name: ExampleApp.TaskSupervisor]]),
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Zadania nadzorowane

Mając uruchomionego nadzorcę możemy użyć funkcji start_child/2, by uruchamiać nadzorowane zadania:

{:ok, pid} = Task.Supervisor.start_child(ExampleApp.TaskSupervisor, fn -> background_work end)

Jeżeli zadanie ulegnie awarii, to zostanie automatycznie zrestartowane. Jest to szczególnie przydatne, gdy pracujemy z zadaniami do obsługi połączeń przychodzących lub pracującymi w tle.


Podziel się