DevGang
Авторизоваться

Понимание стратегий перезапуска процесса: переходные, временные и постоянные

В современном строительстве решающее значение имеют программные системы, способные корректно обрабатывать сбои и поддерживать бесперебойную работу. Elixir, мощный и отказоустойчивый язык программирования, предлагает ряд стратегий управления процессами, когда они сталкиваются с проблемами. Эти стратегии перезапуска процессов, включая :permanent, :temporary и :transient, играют ключевую роль в обеспечении надежности и отказоустойчивости системы. В этом руководстве мы рассмотрим концепции и лучшие практики, лежащие в основе этих стратегий перезапуска, предоставив вам знания для разработки надежных программных систем в Elixir.

Когда какой вариант вы используете?

Есть три варианта :restart:

1. Используйте :permanent, когда:

  • Этот процесс имеет решающее значение для общей работы системы, и его сбой может серьезно повлиять на функциональность системы.
  • Вы хотите, чтобы процесс автоматически перезапускался при невозможности поддерживать доступность системы.
  • Примеры могут включать подключения к базе данных, основные компоненты или критически важные службы.

2. Используйте :temporary, когда:

  • Этот процесс не является существенным для непрерывной работы системы, и его отказ можно допустить без существенных нарушений.
  • Вы хотите свести к минимуму автоматические перезапуски некритических процессов, чтобы избежать чрезмерного использования ресурсов.
  • Примеры могут включать второстепенные фоновые задачи, средства ведения журнала или сборщики метрик.

3. Используйте :transient, когда:

  • Процесс не является критическим сам по себе, но является частью группы процессов или подсистемы, где важна согласованность между процессами.
  • Вы хотите убедиться, что зависимые процессы перезапускаются вместе с временным процессом, чтобы сохранить общую целостность системы.
  • Примеры могут включать рабочие процессы в системе обработки заданий или компоненты распределенной системы.

Эта опция позволяет настроить поведение GenServer при восстановлении в случае сбоя, обеспечивая больший контроль над системой. Таким образом, выбор стратегии перезапуска зависит от критичности процесса для вашей системы и его зависимостей. Тщательное рассмотрение этих факторов поможет вам спроектировать надежные и отказоустойчивые системы, которые смогут эффективно восстанавливаться после сбоев, избегая при этом ненужных перезапусков некритических компонентов.

Использование опции :restart

Чтобы использовать стратегии перезапуска процесса в Elixir, вам обычно приходится работать с деревом контроля, которое представляет собой иерархическую структуру, используемую для управления процессами и контроля над ними. Вот как вы можете использовать различные стратегии перезапуска (:permanent, :temporary и :transient):

1. Создание Supervisor:

  • Сначала вам необходимо создать супервизора с помощью модуля Supervisor. Вы можете использовать Supervisor.start_link/2 или Supervisor.child_spec/2 для настройки супервизора.

2. Добавление дочерних процессов:

  • Затем вы добавляете дочерние процессы (которые могут включать процессы GenServer) в дерево надзора супервизора с помощью функции Supervisor.child_spec/2. В дочерней спецификации вы можете указать стратегию :restart.

3. Определение стратегии перезапуска:

  • В дочерней спецификации вы указываете стратегию :restart. Вы можете установить для него значение :permanent, :temporary или :transient, в зависимости от того, как вы хотите, чтобы процесс перезапускался в случае сбоя.

Вот пример кода того, как можно настроить супервизор с различными стратегиями перезапуска:

defmodule Dummy.Application do
  # See https://hexdocs.pm/elixir/Application.html
  # for more information on OTP Applications
  @moduledoc false

  use Application

  @impl true
  def start(_type, _args) do
    children = [
      # Starts a worker by calling: Dummy.Worker.start_link(arg)
      %{
        id: Dummy.Permanent,
        start: {Dummy.Permanent, :start_link, [[]]},
        restart: :permanent,
        type: :worker
      },
      %{
        id: Dummy.Temporary,
        start: {Dummy.Temporary, :start_link, [[]]},
        restart: :temporary,
        type: :worker
      },
      %{
        id: Dummy.Transient,
        start: {Dummy.Transient, :start_link, [[]]},
        restart: :transient,
        type: :worker
      }
    ]

    # See https://hexdocs.pm/elixir/Supervisor.html
    # for other strategies and supported options
    opts = [
      strategy: :one_for_one,
      name: Dummy.Supervisor
    ]

    Supervisor.start_link(children, opts)
  end
end
Dummy.Permanent
defmodule Dummy.Permanent do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    {:ok, :initial_state}
  end
end
Dummy.Temporary
defmodule Dummy.Temporary do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    {:ok, :initial_state}
  end
end
Dummy.Transient
defmodule Dummy.Transient do
  use GenServer

  def start_link(_) do
    GenServer.start_link(__MODULE__, :ok)
  end

  def init(:ok) do
    {:ok, :initial_state}
  end
end

Давайте запустим IEx и запустим приложение.

$ iex -S mix

Если вы посмотрите, мы проверяем количество детей в Dummy.Supervisor. Воспитатель вздрогнул, и дети тоже. Получение pid и канала для Process.exit/2 с причиной :kill. Проверьте Dummy.Supervisor еще раз. Единственный отдых детей-наставников – Permanent и Transient.

$ iex -S mix
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.127.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.128.0") |> Process.exit(:kill)
true
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 2, workers: 2, supervisors: 0, specs: 2}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Transient, #PID<0.127.0>, :worker, [Dummy.Transient]}
]

Если я воспользуюсь любой причиной, чтобы остановить Transient процесс, он снова вернется живым. Но если причина в :shutdown или {:shutdown, term}, процесс не возобновится, и в этой ситуации мы можем перезапустить процесс вручную, используя Supervisor.restart_child/2. А если я убью временный и попытаюсь перезапустить процесс вручную, то получу кортеж с {:error, :not_found}. Здесь важно то, что я использовал child ID.

iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.127.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.127.0") |> Process.exit(:kill)
true
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.145.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.145.0") |> Process.exit(:normal)
true
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.145.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.145.0") |> Process.exit(:shutdown)
true
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 2, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, :undefined, :worker, [Dummy.Transient]}
]
iex> Supervisor.restart_child(Dummy.Supervisor, Dummy.Transient)
{:ok, #PID<0.146.0>}
iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.146.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.128.0") |> Process.exit(:shutdown)
true
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Transient, #PID<0.146.0>, :worker, [Dummy.Transient]}
]
iex> Supervisor.restart_child(Dummy.Supervisor, Dummy.Temporary)
{:error, :not_found}

И последнее, и не менее важное: когда я играю с Permanent и пытаюсь убить по любой причине, этот процесс всегда возвращается живым.

iex> Supervisor.count_children(Dummy.Supervisor)
%{active: 3, workers: 3, supervisors: 0, specs: 3}
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.129.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.127.0>, :worker, [Dummy.Transient]}
]
iex> pid("0.129.0") |> Process.exit(:shutdown)
true
iex> Supervisor.which_children(Dummy.Supervisor)
[
  {Dummy.Permanent, #PID<0.145.0>, :worker, [Dummy.Permanent]},
  {Dummy.Temporary, #PID<0.128.0>, :worker, [Dummy.Temporary]},
  {Dummy.Transient, #PID<0.127.0>, :worker, [Dummy.Transient]}
]

Заключение

В заключение, стратегии перезапуска процессов в Elixir являются незаменимыми инструментами для создания программных систем, которые могут предоставлять бесперебойные услуги. Применяя такие стратегии, как :permanent, :temporary и :transient. Эти стратегии позволяют нам создавать отказоустойчивые системы, которые корректно восстанавливаются после сбоев, обеспечивая более плавную и надежную работу для конечных пользователей. Продолжая исследовать мир Elixir, сохраняйте эти стратегии перезапуска в своем наборе инструментов.

Ссылки

Источник:

Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу