Erlang GenServer #

一、GenServer概述 #

gen_server是Erlang/OTP中最常用的行为,实现了通用的客户端-服务器模式。

1.1 特点 #

  • 标准化的服务器接口
  • 同步和异步调用
  • 状态管理
  • 错误处理
  • 热代码升级

二、基本结构 #

2.1 完整示例 #

erlang
-module(counter_server).
-behaviour(gen_server).
-export([start_link/0, increment/0, decrement/0, get_count/0, stop/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-define(SERVER, ?MODULE).

start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

increment() ->
    gen_server:cast(?SERVER, increment).

decrement() ->
    gen_server:cast(?SERVER, decrement).

get_count() ->
    gen_server:call(?SERVER, get_count).

stop() ->
    gen_server:stop(?SERVER).

init([]) ->
    {ok, 0}.

handle_call(get_count, _From, State) ->
    {reply, State, State};
handle_call(_Request, _From, State) ->
    {reply, {error, unknown_request}, State}.

handle_cast(increment, State) ->
    {noreply, State + 1};
handle_cast(decrement, State) ->
    {noreply, State - 1};
handle_cast(_Request, State) ->
    {noreply, State}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

三、回调函数 #

3.1 init/1 #

erlang
init(Args) ->
    State = initialize_state(Args),
    {ok, State}.

initialize_state(_Args) -> #{}.

返回值:

  • {ok, State} - 成功初始化
  • {ok, State, Timeout} - 带超时
  • {ok, State, hibernate} - 休眠
  • {stop, Reason} - 初始化失败
  • ignore - 忽略

3.2 handle_call/3 #

erlang
handle_call({get, Key}, _From, State) ->
    Value = maps:get(Key, State, undefined),
    {reply, Value, State};
handle_call({set, Key, Value}, _From, State) ->
    NewState = State#{Key => Value},
    {reply, ok, NewState};
handle_call(stop, _From, State) ->
    {stop, normal, ok, State}.

返回值:

  • {reply, Reply, State} - 返回回复
  • {reply, Reply, State, Timeout} - 带超时
  • {noreply, State} - 不回复
  • {noreply, State, Timeout} - 不回复带超时
  • {stop, Reason, Reply, State} - 停止并回复
  • {stop, Reason, State} - 停止

3.3 handle_cast/2 #

erlang
handle_cast({async_set, Key, Value}, State) ->
    NewState = State#{Key => Value},
    {noreply, NewState};
handle_cast(stop, State) ->
    {stop, normal, State}.

返回值:

  • {noreply, State} - 继续运行
  • {noreply, State, Timeout} - 带超时
  • {stop, Reason, State} - 停止

3.4 handle_info/2 #

erlang
handle_info({timeout, Ref, Msg}, State) ->
    io:format("Timeout: ~p~n", [Msg]),
    {noreply, State};
handle_info({'EXIT', Pid, Reason}, State) ->
    io:format("Process ~p exited: ~p~n", [Pid, Reason]),
    {noreply, State};
handle_info(_Info, State) ->
    {noreply, State}.

3.5 terminate/2 #

erlang
terminate(normal, State) ->
    io:format("Normal termination~n"),
    ok;
terminate(shutdown, State) ->
    io:format("Shutdown~n"),
    ok;
terminate({shutdown, Reason}, State) ->
    io:format("Shutdown: ~p~n", [Reason]),
    ok;
terminate(Reason, State) ->
    io:format("Abnormal termination: ~p~n", [Reason]),
    ok.

3.6 code_change/3 #

erlang
code_change({down, _OldVsn}, State, _Extra) ->
    {ok, downgrade_state(State)};
code_change(_OldVsn, State, _Extra) ->
    {ok, upgrade_state(State)}.

upgrade_state(State) -> State.
downgrade_state(State) -> State.

四、API设计 #

4.1 同步调用 #

erlang
get_value(Key) ->
    gen_server:call(?SERVER, {get, Key}).

set_value(Key, Value) ->
    gen_server:call(?SERVER, {set, Key, Value}).

4.2 异步调用 #

erlang
async_set(Key, Value) ->
    gen_server:cast(?SERVER, {async_set, Key, Value}).

async_stop() ->
    gen_server:cast(?SERVER, stop).

4.3 带超时调用 #

erlang
get_value_with_timeout(Key, Timeout) ->
    gen_server:call(?SERVER, {get, Key}, Timeout).

五、状态管理 #

5.1 使用Map #

erlang
-module(map_state_server).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2]).

init(Args) ->
    {ok, #{args => Args, count => 0}}.

handle_call({get, Key}, _From, State) ->
    {reply, maps:get(Key, State, undefined), State}.

handle_cast({set, Key, Value}, State) ->
    {noreply, State#{Key => Value}}.

5.2 使用Record #

erlang
-module(record_state_server).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2]).

-record(state, {name, count = 0, items = []}).

init(Name) ->
    {ok, #state{name = Name}}.

handle_call(get_name, _From, State = #state{name = Name}) ->
    {reply, Name, State};
handle_call(get_count, _From, State = #state{count = Count}) ->
    {reply, Count, State}.

handle_cast(increment, State = #state{count = Count}) ->
    {noreply, State#state{count = Count + 1}}.

六、实际应用 #

6.1 缓存服务器 #

erlang
-module(cache_server).
-behaviour(gen_server).
-export([start_link/0, get/1, put/2, delete/1, clear/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

get(Key) ->
    gen_server:call(?MODULE, {get, Key}).

put(Key, Value) ->
    gen_server:cast(?MODULE, {put, Key, Value}).

delete(Key) ->
    gen_server:cast(?MODULE, {delete, Key}).

clear() ->
    gen_server:cast(?MODULE, clear).

init([]) ->
    {ok, #{}}.

handle_call({get, Key}, _From, State) ->
    {reply, maps:get(Key, State, undefined), State}.

handle_cast({put, Key, Value}, State) ->
    {noreply, State#{Key => Value}};
handle_cast({delete, Key}, State) ->
    {noreply, maps:remove(Key, State)};
handle_cast(clear, _State) ->
    {noreply, #{}}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

6.2 连接池 #

erlang
-module(pool_server).
-behaviour(gen_server).
-export([start_link/1, checkout/0, checkin/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

start_link(Size) ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [Size], []).

checkout() ->
    gen_server:call(?MODULE, checkout).

checkin(Conn) ->
    gen_server:cast(?MODULE, {checkin, Conn}).

init([Size]) ->
    Connections = [create_connection() || _ <- lists:seq(1, Size)],
    {ok, #{available => Connections, busy => []}}.

handle_call(checkout, _From, State = #{available := []}) ->
    {reply, {error, no_available_connections}, State};
handle_call(checkout, _From, State = #{available := [Conn | Rest], busy := Busy}) ->
    {reply, {ok, Conn}, State#{available => Rest, busy => [Conn | Busy]}}.

handle_cast({checkin, Conn}, State = #{available := Available, busy := Busy}) ->
    NewBusy = lists:delete(Conn, Busy),
    {noreply, State#{available => [Conn | Available], busy => NewBusy}}.

handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

create_connection() ->
    spawn(fun() -> connection_loop() end).

connection_loop() ->
    receive
        {request, From, Data} ->
            From ! {response, Data},
            connection_loop()
    end.

七、总结 #

本章学习了:

  • GenServer概述
  • 基本结构
  • 回调函数
  • API设计
  • 状态管理
  • 实际应用

准备好学习Supervisor了吗?让我们进入下一章。

最后更新:2026-03-27