LoginSignup
3
1

More than 5 years have passed since last update.

DynamicSupervisorで子プロセスを増やしたり減らしたりする

Last updated at Posted at 2018-07-14

やりたいこと

  • GenServerをたくさん作りたい
  • UUIDなどで一意に管理したい

使うもの

  • DynamicSupervisor
  • Registry
  • GenServer

実装

プロセスを用意する

ここではGenServerを用意します。挨拶をする関数とクラッシュする関数を用意しました。
start_link/1 関数はあとで必要になります。argsの使い方が適当ですが、今回はname:オプションだけが大事なのだ
init/1 もいらないですが、書かなかったときのwarningが長ったらしいので書いておきます。

defmodule GS do
  use GenServer

  def start_link(args) do
    opts = args |> Keyword.take([:name])
    GenServer.start_link(__MODULE__, args, opts)
  end

  def init(args), do: {:ok, args}

  def handle_call(:hello, _from, state), do: {:reply, "hello", state}
  def handle_cast(:crash, state), do: raise "Crashed"
end

Supervisorをつくる

子プロセス(先程のGS)を立ち上げたり落としたりする関数を用意します
その際Registryを利用します
:via タプルをGSのnameオプションに渡るようにします

defmodule DSV do
  use DynamicSupervisor

  @registry Registry.ViaTest

  def start_link(args), do: DynamicSupervisor.start_link(__MODULE__, args, name: __MODULE__)
  def init(_), do: DynamicSupervisor.init(strategy: :one_for_one)

  def add_child(name) when name |> is_binary do
    pid = child_process(name)
    spec = GS.child_spec(name: pid)
    DynamicSupervisor.start_child(__MODULE__, spec)
  end

  def remove_child(name) when name |> is_binary do
    [{pid, _}] = Registry.lookup(@registry, name)
    :ok = DynamicSupervisor.terminate_child(__MODULE__, pid)

    ## ↓これ要りませんでした!@melponさんありがとうございます
    # Registry.unregister(Registry.ViaTest, name)
  end

  # :via tupleをつくる
  def child_process(name), do: {:via, Registry, {@registry, name}}
end

あそぶ

# Setup
Registry.start_link(keys: :unique, name: Registry.ViaTest)
DSV.start_link([])

# Create child with unique name
name = "todoroki"
DSV.add_child(name)

# communicate!
name |> DSV.child_process |> GenServer.call(:hello)  # Hello

# crashes and restarts!
name |> DSV.child_process |> GenServer.cast(:term)   # :term

# communicate again!
name |> DSV.child_process |> GenServer.call(:hello)  # Hello

# remove the child
DSV.remove_child(name)

# now you can not talk
name |> DSV.child_process |> GenServer.call(:hello)  # エラー

所感

  • DynamicSupervisorの子Workerを立ち上げたり消したりできた
  • DynamicSupervisor.terminate_child/2{:via, ...} タプルでできればいいのになーと思った
    • (DynamicSupervisor自体には子のpidを取ってくる方法がないので)
3
1
1

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
3
1