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_serverbehavior. 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_servercallback 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/1handle_call/3handle_cast/2handle_info/2terminate/2code_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)(whereModis 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,Replygoes back to the client, where it becomes the return value ofgen_server:call.NewStateis the next state of the server.The other return values,
{noreply, ...}and{stop, ...}are used relatively infrequently.noreplycauses 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. Callingstopwith 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:callorgen_server:cast. (Armstrong 2013, 372)
handle_info(_Info, State) ->
{noreply, State}.
Termination
Stopping the server requires the callbacks to return different tuples:
init/1can return{stop, Reason}handle_call/3can return{stop, Reason, Reply, LoopData}handle_cast/2can return{stop, Reason, LoopData}handle_info/2can 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/2is 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_servermanaged by a simple one-for-one Supervisor. (…) . In this case, a singlegen_serverchild process — a handler —is initially spawned to wait on accept, listening for new connections. When a connection is established, thisgen_servertells thesupervisorto 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.