LoginSignup
1
0

More than 3 years have passed since last update.

すごいE本 第17章 後半戦 on Elixir (DynamicSupervisor)

Last updated at Posted at 2019-05-02

環境

sh
$ lsb_release -d
Description:    Ubuntu 18.04.2 LTS

$ elixir -v
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]

Elixir 1.8.1 (compiled with Erlang/OTP 20)

17.3 バンドで練習

simple_one_for_one スーパバイザを使う

:simple_one_for_one のとき、子の仕様(全て一律)は辞書で保持されるらしい。
それ以外の戦略の場合はリストで保持されるので子が多い場合は前者がいいんだね。

Supervisor.start_child(sup, [arg, ...]) とすると、
:simple_one_for_onesup の子仕様 start: {mod, :start_link, args} を探し、
apply(mod, :start_link, args++[arg, ...]) を呼び出す。
ややこいね。

Elixir では非推奨だけどやってみよう。

band/lib/band.ex
defmodule Band do
  use Supervisor
  alias __MODULE__, as: Me
  alias __MODULE__.Musician
  ...
  # 追加
  @impl true
  def init(:jamband) do
    child = %{
      # 親が子を識別するための child_id
      id: :jam_musician,
      start: {Musician, :start_link, []},
      restart: :temporary,
    }

    Supervisor.init([child], strategy: :simple_one_for_one)
  end
  ...
end
iex
iex(1)> Band.start_link(:jamband)
{:ok, #PID<0.147.0>}
iex(2)> Supervisor.start_child(Band, [:djembe, :good])
Dorothy Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.149.0>}
Dorothy Frizzle(good djembe) の音はいいね!
...
iex(3)> Supervisor.start_child(Band, [:djembe, :good])
{:error, {:already_started, #PID<0.149.0>}}
...

Supervisor.start_chid(Band, [:djembe, :good]) が、
apply(Band.Musician, :start_link, []++[:djembe, :good]) を呼び出す。

Musician の定義を思い出すと、この :djembe で名前を登録するのであった。
なわけで、2回目の start_child/2 で名前衝突が起きている。
こういう状況に便利な Registry というものがあるが、
話が長くなるので、ここでは E本の流れに準じ単に違うアトムを渡す。

iex
...
iex(4)> Supervisor.start_child(Band, [:drum, :good])
Wanda Perlstein(good drum) は部屋に入った。
{:ok, #PID<0.152.0>}
...
iex(5)> Supervisor.start_child(Band, [:guitar, :good])
Phoebe Li(good guitar) は部屋に入った。
{:ok, #PID<0.154.0>}
...
iex(6)> Supervisor.terminate_child(Band, :djembe)
{:error, :simple_one_for_one} # あれれ?
...
iex(7)> Band.Musician.stop(:djembe)
Dorothy Frizzle(good djembe) は部屋を出た。
:ok
...

うまい話ばかりじゃないですよ

うーん、話が食い違うねー。
Erlang 20 でコンパイルされた Elixir なんだけど、どーして?

iex
...
iex(6)> Supervisor.terminate_child(Band, Process.whereis(:djembe))
マネージャーは怒りバンドを解散させた!
Dorothy Frizzle(good djembe) は地下鉄へ去っていった。

:ok

誤植に耐えてよく頑張った!感動した!

DynamicSupervisor を使ってみる

DynamicSupervisor — Elixir vx.x.x

Supervisor から :simple_one_for_one を排除したいのね。
別のモジュールにしちゃった方が見通しが良いでしょということかな。

sh
$ touch lib/band/jam.ex
band/lib/band/jam.ex
defmodule Band.Jam do
  use DynamicSupervisor
  alias __MODULE__, as: Me
  alias Band.Musician

  ### use で child_spec/1 が自動的に定義される。 ###
  # def child_spec(arg) do
  #   %{
  #     id: __MODULE__,
  #     start: {__MODULE__, :start_link, [arg]},
  #     type: supervisor
  #   }
  # end

  def start_link do
    DynamicSupervisor.start_link(Me, [], name: Me)
  end

  def stop, do: DynamicSupervisor.stop(Me)

  def start_child(role, skill) do
    child = %{
      id: Musician,
      start: {Musician, :start_link, [role, skill]},
      restart: :temporary
    }

    DynamicSupervisor.start_child(Me, child)
  end

  def terminate_child(role) do
    pid = Process.whereis(role)
    DynamicSupervisor.terminate_child(Me, pid)
  end

  @impl true
  def init([]) do
    # 子仕様リストとして返却しなくて済むようになった。
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end
iex
iex(1)> Band.Jam.start_link()
{:ok, #PID<0.166.0>}
iex(2)> Band.Jam.start_child(:djembe, :good)
Carlos Frizzle(good djembe) は部屋に入った。
{:ok, #PID<0.168.0>}
Carlos Frizzle(good djembe) の音はいいね!
...
iex(3)> Band.Jam.start_child(:drum, :good)
Dorothy Li(good drum) は部屋に入った。
{:ok, #PID<0.170.0>}
...
iex(4)> Band.Jam.start_child(:guitar, :good)
Wanda Terese(good guitar) は部屋に入った。
{:ok, #PID<0.172.0>}
...
iex(5)> Band.Jam.terminate_child(:djembe)
マネージャーは怒りバンドを解散させた!
Carlos Frizzle(good djembe) は地下鉄へ去っていった。

:ok
...
iex(6)> Band.Jam.stop()
マネージャーは怒りバンドを解散させた!
Dorothy Li(good drum) は地下鉄へ去っていった。

マネージャーは怒りバンドを解散させた!
Wanda Terese(good guitar) は地下鉄へ去っていった。

:ok

うむ、言語設計者の哲学を感じるね。

動的に子を追加していく場合、今回の様にタプルで名前をつけていくのはよろしくない。
タプルはガベージコレクションの対象ではないからだ。
自前で対策を踏むよりも Registry を利用した方が良さそうだ。
だが、この章の段階では手を出すには早すぎるだろう。

次章からはアプリケーションだ。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0