Back

elixir - 22 genserver

发布时间: 2019-03-05 08:15:00

参考:https://elixir-lang.org/getting-started/mix-otp/genserver.html

新建一个 registry.ex文件

efmodule KV.Registry do
  use GenServer

  def start_link(opts) do
    # 这里的__MODULE__ 表示当前的 module
    GenServer.start_link(__MODULE__, :ok, opts)
  end
    
  def lookup(server,name) do
    GenServer.call(server, {:lookup, name})
  end

  def create(server, name) do
    GenServer.cast(server, {:create, name})
  end

  def init(:ok) do
    {:ok , %{}}
  end

  # call  : 是同步的 sync
  def handle_call({:lookup,name}, _from, names) do
    {:reply, Map.fetch(names, name), names}
  end

  # cast: 是异步的(async)
  def handle_cast({:create, name}, names ) do
    if Map.has_key?(names, name) do
      {:noreply, names}

    else
      {:ok, bucket} = KV.Bucket.start_link([])
      {:noreply, Map.put(names, name, bucket)}
    end
  end
end

测试一下 Agent, Process monitor

$ iex -S mix
iex(1)> {:ok, pid} = KV.Bucket.start_link []
{:ok, #PID<0.153.0>}
iex(2)> Process.monitor pid
#Reference<0.1655961756.1033633793.180708>
iex(3)> Agent.stop pid
:ok
iex(4)> flush
{:DOWN, #Reference<0.1655961756.1033633793.180708>, :process, #PID<0.153.0>,
 :normal}
:ok
还可以进一步修改 server的实现, 代码如下:
defmodule KV.Registry do
  use GenServer

  def start_link(opts) do
    # 这里的__MODULE__ 表示当前的 module
    GenServer.start_link(__MODULE__, :ok, opts)
  end

  def lookup(server,name) do
    GenServer.call(server, {:lookup, name})
  end

  def create(server, name) do
    GenServer.cast(server, {:create, name})
  end

  def init(:ok) do
    names = %{}
    refs = %{}
    {:ok, {names, refs}}
  end

  # call  : 是同步的 sync
  def handle_call({:lookup,name}, _from, state ) do
    {names, _} = state
    {:reply, Map.fetch(names, name), state}
  end

  # cast: 是异步的(async)
  def handle_cast({:create, name}, {names, refs}) do
    if Map.has_key?(names, name) do
      {:noreply, {names, refs}}

    else
      {:ok, pid} = KV.Bucket.start_link([])
      ref = Process.monitor(pid)
      refs = Map.put(refs, ref, name)
      names = Map.put(names, name, pid)
      {:noreply, {names, refs}}
    end
  end

  def handle_info({:DOWN, ref, :process, _pid, _reason}, {names, refs}) do
    {name, refs} = Map.pop(refs, ref)
    names = Map.delete(names, name)
    {:noreply, {names, refs}}
  end

  def handle_info(_msg, state) do
    {:noreply, state}
  end

  def stop(server) do
    GenServer.stop(server)
  end
end

Back