基本のソース
defmodule NumStore do
use Application
def start(_type, _args) do
import Supervisor.Spec, warn: false
children = [
worker(NumStore.Worker, []),
]
opts = [strategy: :one_for_one, name: NumStore.Supervisor]
Supervisor.start_link(children, opts)
end
end
defmodule NumStore.Worker do
use GenServer
# Callbacks
def start_link() do
GenServer.start_link(__MODULE__, 0, name: __MODULE__)
end
def init (state) do
:global.register_name :server, self() # <= 適当に名前を登録
{:ok, state}
end
def handle_cast(:succ, state) do
{:noreply, state+1}
end
def handle_call(:current, _from, state) do
{:reply, state, state}
end
end
サーバー側起動
succで状態の内容を+1。currentで値を取得する。
# elixirコマンドの場合は--no-halt。
$ iex --name server@127.0.0.1 -S mix
iex(server@127.0.0.1)1> pid = :global.whereis_name(:server)
#PID<0.XXXX.0>
iex(server@127.0.0.1)2> :gen_server.cast(pid, :succ)
:ok
iex(server@127.0.0.1)3> :gen_server.call(pid, :current)
1
別nodeから接続
$ iex --name client@127.0.0.1
iex(client@127.0.0.1)1> Node.connect(:"server@127.0.0.1")
true
iex(client@127.0.0.1)2> :global.sync() <= elixirコマンドでやる場合は必要。
:ok
iex(client@127.0.0.1)3> Node.list
[:"server@127.0.0.1"]
別nodeからメッセージを送信
公式ドキュメントによると、
サーバーのpidか名前とnodeがあればいいらしい。
iex(client@127.0.0.1)4> pid = :global.whereis_name(:server)
#PID<XXXX.XX.0>
iex(client@127.0.0.1)5> :gen_server.call(pid, :current)
1
iex(client@127.0.0.1)6> :gen_server.cast(pid, :succ)
:ok
iex(client@127.0.0.1)7> :gen_server.call(pid, :current)
2
iex(client@127.0.0.1)8> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.cast(:succ)
:ok
iex(client@127.0.0.1)9> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.call(:current)
3
別nodeから変更
Module.eval_quotedで頑張ってみたけど、出来なかった。
再定義以外にないものか・・・。
iex(client@127.0.0.1)10> Node.spawn :"server@127.0.0.1", fn ->
...(client@127.0.0.1)10> defmodule NumStore.Worker do
...(client@127.0.0.1)10> use GenServer
...(client@127.0.0.1)10> def start_link() do
...(client@127.0.0.1)10> GenServer.start_link(__MODULE__, 0, name: __MODULE__)
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> def init (state) do
...(client@127.0.0.1)10> :global.register_name :server, self()
...(client@127.0.0.1)10> {:ok, state}
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> def handle_cast(:minus, state) do # <= 追加
...(client@127.0.0.1)10> {:noreply, state-1}
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> def handle_cast(:succ, state) do
...(client@127.0.0.1)10> {:noreply, state+1}
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> def handle_call(:current, _from, state) do
...(client@127.0.0.1)10> {:reply, state, state}
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> end
...(client@127.0.0.1)10> end
iex(client@127.0.0.1)11> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.cast(:minus)
:ok
iex(client@127.0.0.1)12> ({NumStore.Worker, :"server@127.0.0.1"}) |> :gen_server.call(:current)
2
まとめ
サーバー(NumStore.Worker)の定義を上書きしても状態保持しているのが謎技術。