Fork me on GitHub

Nadzorcy OTP

Some contents of this translation may be outdated.
Several major changes were applied to the original lesson since the last update.

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ę