ヽ(´・肉・`)ノログ

How do we fighting without fighting?

simple_one_for_oneのメリット

Supervisor起動戦略

Supervisor の子プロセスに関する振舞いには

  1. one_for_one
  2. one_for_all
  3. rest_for_one
  4. simple_one_for_one

の 4 つがある.

Supervisorのプロセス生成時に監視対象のプロセスも生成する

1-3 はスーパーバイザー起動時に子プロセス生成のための情報を渡しておき, スーパーバイザーのプロセスを生成すると,自動的に子プロセスを生成してくれるものだ.

以下のコードでも,スーパーバイザー Foo.Supervisor.start_link/0 で生成すると,自動的に Foo.start_link/2 が呼ばれているのがわかるだろう.

defmodule Foo do
  use GenServer

  def start_link(state, opts \\ []) do
    IO.puts "Foo.start_link/2, state: #{inspect state}, opts: #{inspect opts}"
    GenServer.start_link(__MODULE__, state, opts)
  end
end

defmodule Foo.Supervisor do
  use Supervisor

  def start_link do
    IO.puts "Foo.Supervisor.start_link/0"
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init(args) do
    IO.puts "Foo.Supervisor.init/1, args: #{inspect args}"
    children = [
      worker(Foo, [[:hello]])
    ]

    IO.puts "start supervise, children: #{inspect children}"
    supervise(children, strategy: :one_for_one)
  end
end

Foo.Supervisor.start_link
#> Foo.Supervisor.start_link/0
#> Foo.Supervisor.init/1, args: []
#> start supervise, children: [{Foo, {Foo, :start_link, [[:hello]]}, :permanent, 5000, :worker, [Foo]}]
#> Foo.start_link/2, state: [:hello], opts: []

Supervisorのプロセス生成時とは別に監視対象のプロセスを生成する

4 の simple_one_for_one だけは,監視対象のプロセスを生成するタイミングが異なり, スーパーバイザーのプロセスを生成しても,自動的に子プロセスを生成してくれはしない.

以下のコードで Foo.start_link/2 が呼ばれなくなっているのがわかるだろう.

defmodule Foo do
  use GenServer

  def start_link(state, opts \\ []) do
    IO.puts "Foo.start_link/2, state: #{inspect state}, opts: #{inspect opts}"
    GenServer.start_link(__MODULE__, state, opts)
  end
end

defmodule Foo.Supervisor do
  use Supervisor

  def start_link do
    IO.puts "Foo.Supervisor.start_link/0"
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  def init(args) do
    IO.puts "Foo.Supervisor.init/1, args: #{inspect args}"
    children = [
      # worker(Foo, [[:hello]]) から変更
      worker(Foo, [])
    ]

    IO.puts "start supervise, children: #{inspect children}"
    # supervise(children, strategy: :one_for_one) から変更
    supervise(children, strategy: :simple_one_for_one)
  end
end

Foo.Supervisor.start_link
#> Foo.Supervisor.start_link/0
#> Foo.Supervisor.init/1, args: []
#> start supervise, children: [{Foo, {Foo, :start_link, []}, :permanent, 5000, :worker, [Foo]}]

simple_one_for_oneで子プロセスをどのように生成するか?

Supervisor.start_child/2 を呼ぶと生成される.

defmodule Foo do
  use GenServer

  def start_link(state, opts \\ []) do
    IO.puts "Foo.start_link/2, state: #{inspect state}, opts: #{inspect opts}"
    GenServer.start_link(__MODULE__, state, opts)
  end
end

defmodule Foo.Supervisor do
  use Supervisor

  def start_link do
    IO.puts "Foo.Supervisor.start_link/0"
    Supervisor.start_link(__MODULE__, [], name: __MODULE__)
  end

  def start_child(args) do
    Supervisor.start_child(__MODULE__, args)
  end

  def init(args) do
    IO.puts "Foo.Supervisor.init/1, args: #{inspect args}"
    children = [
      worker(Foo, [])
    ]

    IO.puts "start supervise, children: #{inspect children}"
    supervise(children, strategy: :simple_one_for_one)
  end
end

Foo.Supervisor.start_link
#> Foo.Supervisor.start_link/0
#> Foo.Supervisor.init/1, args: []
#> start supervise, children: [{Foo, {Foo, :start_link, []}, :permanent, 5000, :worker, [Foo]}]

Foo.Supervisor.start_child([[:foo]])
#> Foo.start_link/2, state: [:foo], opts: []

Foo.Supervisor.start_child([[:bar]])
#> Foo.start_link/2, state: [:bar], opts: []

スーパーバイザーのプロセスを生成した後,任意のタイミングで監視対象の子プロセスを生成できると何が嬉しいのだろうか?

それは子プロセスの生成時に,任意のパラメータを受けとれるようになることだ. こうすると,動的に生成したプロセスを監視することができる.

例えば,リクエストを受けつけるような Web サーバーのプロセスを生成しておき,リクエストは simple_one_for_one で処理するようにしておく. すると,リクエストの処理中にエラーが起きてプロセスが死んだり,特定のリクエスト処理が長時間かかっても,Web サーバーのプロセスや,他のリクエストのプロセスには影響を及ぼさない.

こういった処理をすることは良くあるだろうし,その場合に便利だろう.

具体例が やってみた -URL外形監視- にあるので参考にするとよい.

まとめ

Supervisor の子プロセスに関する振舞いは,大きく 2 つにわけられる.

  1. Supervisorのプロセス生成時に監視対象のプロセスも生成する
  2. Supervisorのプロセス生成時とは別に監視対象のプロセスを生成する

このうち,後者を行うための戦略 simple_one_for_one のメリット,「任意のパラメータを与えて子プロセスを生成できる」について書いた.

このエントリーをはてなブックマークに追加