LoginSignup
9
6

More than 5 years have passed since last update.

elixirのGenServerで色々実験してみた。

Last updated at Posted at 2015-11-24

基本のソース

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)の定義を上書きしても状態保持しているのが謎技術。

9
6
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
9
6