Edited at

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

More than 1 year has passed since last update.


やりたいこと


  • 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を取ってくる方法がないので)