やりたいこと
- 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を取ってくる方法がないので)