elixir 的gen_server

      在 erlang的OTP中,gen_server作为通用服务器,是使用频率很高也很好用的一个行为模式,而elixir的底层就是erlang,自然逃不过OTP,包括其中的sup,gen—server等行为模式。

下面就看下在elixir中,它的语法和erlang的gen_server 有什么不同,看elixir的gen_server 是如何写的

 

一个GenServer实现分为两个部分:客户端API和服务端回调函数。
这两部分可以写在同一个模块里,也可以分开写到两个模块中。
客户端和服务端运行于不同进程,依靠调用客户端函数来与服务端来回传递消息。
方便起见,这里我们将这两部分写在一个模块中。

创建文件registry.ex,包含以下内容:

defmodule GenServerTest do
  use GenServer
  ## Client API
  @doc """
  Starts the registry.
  """
    def start_link() do
      GenServer.start_link(__MODULE__, :ok, [])
    end
  @doc """
  Looks up the bucket pid for `name` stored in `server`.
  Returns `{:ok, pid}` if the bucket exists, `:error` otherwise.
  """

    def lookup(server, name) do
      GenServer.call(server, {:lookup, name})
    end
  @doc """
  Ensures there is a bucket associated to the given `name` in `server`.
  """
    def create(server, name) do
      GenServer.cast(server, {:create, name})
    end
  ## Server Callbacks
    def init(:ok) do
      {:ok, %{}}
    end

    def handle_call({:lookup, name}, _from, names) do
        {:reply, Map.fetch(names, name), names}
    end

    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

 

第一个函数是start_link/0,它传递三个参数启动了一个新的GenServer:

  1. 实现了服务器回调函数的模块名称。这里的__MODULE__指的是当前模块
  2. 初始参数,这里是:ok
  3. 一组选项列表,比如可以存放服务器的名字。这里用个空列表

你可以向一个GenServer发送两种请求:callcastCall 是同步的,
服务器 必须 发送回复给该类请求。Cast 是异步的,服务器 不会 发送回复消息。

再往下的两个方法,lookup/2create/2,它们用来发送这些请求给服务器。
这两种请求,会被第一个参数所指认的服务器中的handle_call/3handle_cast/2
函数处理(因此你的服务器回调函数必须包含这两个函数)。GenServer.call/2
GenServer.cast/2除了指认服务器之外,还告诉服务器它们要发送的请求。


这个请求存储在元组里,这里即{:lookup, name}{:create, name}
在下面写相应的回调处理函数时会用到。
这个消息元组第一个元素一般是要服务器做的事儿,后面的元素就是该动作的参数。

在服务器这边,我们要实现一系列服务器回调函数来实现服务器的启动、停止以及处理请求等。
回调函数是可选的,我们在这里只实现所关心的那几个。

第一个是init/1回调函数,它接受一个状态参数(你在用户API中调用GenServer.start_link/3中使用的那个),
返回{:ok, state}。这里state是一个新建的map。
我们现在已经可以观察到,GenServer的API中,客户端和服务器之间的界限十分明显。start_link/3在客户端发生。
而其对应的init/1在服务器端运行。

对于call请求,我们在服务器端必须实现handle_call/3回调函数。
参数:接收某请求(那个元组)、请求来源(_from)以及当前服务器状态(names)。handle_call/3函数返回一个{:reply, reply, new_state}元组。
其中,reply是你要回复给客户端的东西,而new_statue是新的服务器状态。

对于cast请求,我们必须实现一个handle_cast/2回调函数,

接受参数:request以及当前服务器状态(names)。
这个函数返回{:noreply, new_state}形式的元组。

这两个回调函数,handle_call/3handle_cast/2还可以返回其它几种形式的元组。
还有另外几种回调函数,如terminate/2code_change/3等。
可以参考完整的GenServer文档来学习相关知识。

 

posted @ 2022-06-08 16:25  孤独信徒  阅读(86)  评论(0编辑  收藏  举报