Elixir GenServer #
一、GenServer概述 #
1.1 什么是GenServer #
GenServer是Elixir中最常用的OTP行为,实现了客户端-服务器模式。
1.2 GenServer特性 #
- 同步和异步调用
- 状态管理
- 超时处理
- 热代码升级
- 监督树集成
二、实现GenServer #
2.1 基本结构 #
elixir
defmodule Counter do
use GenServer
def start_link(initial \\ 0) do
GenServer.start_link(__MODULE__, initial, name: __MODULE__)
end
def init(initial) do
{:ok, initial}
end
def increment do
GenServer.cast(__MODULE__, :increment)
end
def get do
GenServer.call(__MODULE__, :get)
end
def handle_cast(:increment, state) do
{:noreply, state + 1}
end
def handle_call(:get, _from, state) do
{:reply, state, state}
end
end
2.2 启动GenServer #
elixir
iex(1)> Counter.start_link(0)
{:ok, #PID<0.123.0>}
iex(2)> Counter.increment()
:ok
iex(3)> Counter.get()
1
三、回调函数 #
3.1 init #
elixir
def init(args) do
{:ok, state}
end
def init(args) do
{:stop, reason}
end
def init(args) do
:ignore
end
3.2 handle_call #
处理同步调用:
elixir
def handle_call(:get, _from, state) do
{:reply, state, state}
end
def handle_call({:set, new_state}, _from, _state) do
{:reply, :ok, new_state}
end
def handle_call(:stop, _from, state) do
{:stop, :normal, :ok, state}
end
返回值:
{:reply, reply, new_state}{:reply, reply, new_state, timeout | :hibernate}{:noreply, new_state}{:noreply, new_state, timeout | :hibernate}{:stop, reason, reply, new_state}{:stop, reason, new_state}
3.3 handle_cast #
处理异步调用:
elixir
def handle_cast(:increment, state) do
{:noreply, state + 1}
end
def handle_cast({:set, new_state}, _state) do
{:noreply, new_state}
end
def handle_cast(:stop, state) do
{:stop, :normal, state}
end
返回值:
{:noreply, new_state}{:noreply, new_state, timeout | :hibernate}{:stop, reason, new_state}
3.4 handle_info #
处理其他消息:
elixir
def handle_info(:tick, state) do
IO.puts("Tick: #{state}")
{:noreply, state}
end
def handle_info({:DOWN, _ref, :process, _pid, _reason}, state) do
{:noreply, state}
end
def handle_info(_msg, state) do
{:noreply, state}
end
3.5 terminate #
elixir
def terminate(reason, state) do
IO.puts("Terminating: #{reason}")
:ok
end
3.6 code_change #
elixir
def code_change(_old_vsn, state, _extra) do
{:ok, state}
end
四、API设计 #
4.1 完整示例 #
elixir
defmodule Stack do
use GenServer
def start_link(initial \\ []) do
GenServer.start_link(__MODULE__, initial, name: __MODULE__)
end
def push(item) do
GenServer.cast(__MODULE__, {:push, item})
end
def pop do
GenServer.call(__MODULE__, :pop)
end
def peek do
GenServer.call(__MODULE__, :peek)
end
def clear do
GenServer.cast(__MODULE__, :clear)
end
def size do
GenServer.call(__MODULE__, :size)
end
def init(initial) do
{:ok, initial}
end
def handle_cast({:push, item}, state) do
{:noreply, [item | state]}
end
def handle_cast(:clear, _state) do
{:noreply, []}
end
def handle_call(:pop, _from, [head | tail]) do
{:reply, head, tail}
end
def handle_call(:pop, _from, []) do
{:reply, nil, []}
end
def handle_call(:peek, _from, [head | _] = state) do
{:reply, head, state}
end
def handle_call(:peek, _from, []) do
{:reply, nil, []}
end
def handle_call(:size, _from, state) do
{:reply, length(state), state}
end
end
4.2 使用示例 #
elixir
iex(1)> Stack.start_link([1, 2, 3])
{:ok, #PID<0.123.0>}
iex(2)> Stack.peek()
1
iex(3)> Stack.pop()
1
iex(4)> Stack.push(0)
:ok
iex(5)> Stack.size()
3
五、超时与hibernate #
5.1 超时 #
elixir
def handle_call(:slow, _from, state) do
Process.sleep(5000)
{:reply, :ok, state}
end
def handle_info(:timeout, state) do
IO.puts("Timeout!")
{:noreply, state}
end
5.2 hibernate #
elixir
def handle_call(:get, _from, state) do
{:reply, state, state, :hibernate}
end
六、命名注册 #
6.1 原子名称 #
elixir
GenServer.start_link(__MODULE__, [], name: :my_server)
GenServer.call(:my_server, :get)
6.2 全局名称 #
elixir
GenServer.start_link(__MODULE__, [], name: {:global, :my_server})
GenServer.call({:global, :my_server}, :get)
6.3 via注册 #
elixir
defmodule Registry do
def start_link do
Registry.start_link(keys: :unique, name: MyApp.Registry)
end
end
GenServer.start_link(__MODULE__, [], name: {:via, Registry, {MyApp.Registry, :my_server}})
GenServer.call({:via, Registry, {MyApp.Registry, :my_server}}, :get)
七、监控其他进程 #
7.1 监控进程 #
elixir
def handle_call({:monitor, pid}, _from, state) do
ref = Process.monitor(pid)
{:reply, :ok, Map.put(state, ref, pid)}
end
def handle_info({:DOWN, ref, :process, pid, reason}, state) do
IO.puts("Process #{inspect(pid)} down: #{reason}")
{:noreply, Map.delete(state, ref)}
end
八、定时任务 #
8.1 使用Process.send_after #
elixir
def init(args) do
schedule_work()
{:ok, args}
end
def handle_info(:work, state) do
do_work()
schedule_work()
{:noreply, state}
end
defp schedule_work do
Process.send_after(self(), :work, 60_000)
end
九、最佳实践 #
9.1 API封装 #
elixir
defmodule MyServer do
use GenServer
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def operation(arg) do
GenServer.call(__MODULE__, {:operation, arg})
end
def async_operation(arg) do
GenServer.cast(__MODULE__, {:async_operation, arg})
end
end
9.2 错误处理 #
elixir
def handle_call({:divide, a, b}, _from, state) do
try do
result = a / b
{:reply, {:ok, result}, state}
rescue
ArithmeticError ->
{:reply, {:error, :division_by_zero}, state}
end
end
9.3 状态验证 #
elixir
def handle_call({:set, new_state}, _from, state) do
if valid?(new_state) do
{:reply, :ok, new_state}
else
{:reply, {:error, :invalid_state}, state}
end
end
十、总结 #
本章学习了:
| 回调 | 用途 |
|---|---|
init/1 |
初始化 |
handle_call/3 |
同步调用 |
handle_cast/2 |
异步调用 |
handle_info/2 |
其他消息 |
terminate/2 |
终止处理 |
code_change/3 |
热升级 |
准备好学习Supervisor了吗?让我们进入下一章。
最后更新:2026-03-27