Gen Server
Much of the work you think of as the core of a program - calculating results, storing information, and preparing replies - will fit neatly into the
gen_server
behavior. It provides a core set of methods that let you set up a process, respond to requests, end the process gracefully, and even pass state to a new process if this one needs to be upgraded in place. (Laurent 2017, 148)
gen_server
is a generic server process that implements a standard set of
interface functions and functionality for tracing and error reporting, it also
fits an OTP supervision tree.
Here’s the simple three-point plan for writing a
gen_server
callback module:
- Decide on a callback module name.
- Write the interface functions.
- Write the six required callback functions in the callback module.
Callback Structure
The gen_server
behaviour interface contains six callback functions:
init/1
handle_call/3
handle_cast/2
handle_info/2
terminate/2
code_change/3
Starting the Server (init/1
)
gen_server:start_link(Name, Mod, InitArgs, Opts)
starts the server created by the gen_server:start_link/4
call:
- The generic server starts by calling
Mod:init(InitArgs)
(whereMod
is the name of the callback module). If{ok, State}
is returned, then the server is successfully started the server with an initial state. - If the first argument was atom
{global, Name}
, it would start a global server that could be accessed on a cluster of Erlang nodes.
init([]) -> {ok, #state{}}.
Calling the Server
handle_call/3
Handles synchronous requests sent to the server by gen_server:call/2
.
gen_server:call(?MODULE, Term)
is used for a remote procedure call to the server.
handle_call(_Request, _From, State) -> Reply = ok, {reply, Reply, State}.
Normally we return
{reply, Reply, NewState}
. When this happens,Reply
goes back to the client, where it becomes the return value ofgen_server:call
.NewState
is the next state of the server.The other return values,
{noreply, ...}
and{stop, ...}
are used relatively infrequently.noreply
causes the server to continue, but the client will wait for a reply so the server will have to delegate the task of replying to some other process. Callingstop
with the appropriate arguments will stop the server. (Armstrong 2013, 372)
Call Timeouts
gen_server:call(Server, Message, TimeOut) -> Reply
handle_cast/2
To send an asynchronous message to a gen_server
process, the requests originate
in the gen_server:cast/2
call, which sends a message to a server process and
immediately returns.
handle_cast(_Msg, State) -> {noreply, State}.
The handler usually just returns {noreply, NewState}
, which changes the state of
the server, or {stop, ...}
, which stops the server.
Spontaneous Messages to the Server
The callback function
handle_info(Info, State)
is used for handling spontaneous messages to the server. Spontaneous messages are any messages that arrive at the server that were not sent by explicitly callinggen_server:call
orgen_server:cast
. (Armstrong 2013, 372)
handle_info(_Info, State) -> {noreply, State}.
Termination
Stopping the server requires the callbacks to return different tuples:
init/1
can return{stop, Reason}
handle_call/3
can return{stop, Reason, Reply, LoopData}
handle_cast/2
can return{stop, Reason, LoopData}
handle_info/2
can return{stop, Reason, LoopData}
These return values terminate with the same behavior as if
exit(Reason)
were called. In the case of calls and casts, before exiting, the callback functionterminate(Reason, LoopData)
is called. It allows the server to clean up after itself before being shut down. Any value returned byterminate/2
is ignored.
terminate(_Reason, _State) -> ok.
Code Change
The callback code_change/3
function is called by the release handling subsystem
when the system performs a software upgrade.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
Patterns
Efficient TCP Server
A useful pattern for implementing a server that should handle multiple concurrent requests is to have a
gen_server
managed by a simple one-for-one Supervisor. (…) . In this case, a singlegen_server
child process — a handler —is initially spawned to wait on accept, listening for new connections. When a connection is established, thisgen_server
tells thesupervisor
to spawn a new handler process — a clone of thegen_server
— and immediately proceeds with servicing the current connection while the clone takes over the job of waiting for the next connection. (Logan, Merritt, and Carlsson 2010, 262)
Generic Server Timeouts
Hibernate Behaviour
If instead of a timeout value or the atom infinity we return the atom hibernate, the server will reduce its memory footprint and enter a wait state. You will want to use hibernate when servers that receive intermittent, memory-intensive requests are causing the system to run low on memory.