This chapter should be read in conjunction with gen_server (3). It describes all interface functions and callback functions in detail.
Client-server Principle
The client-server (C/S) model features a central server and any number of clients. The C/S model is usually used for resource management operations. Some different clients need to share a public resource. The server is responsible for managing these resources.
Example
InOverviewThere is already a simple server written in the normal Erlang mode. This server can be usedgen_serverThe callback module is generated as follows:
-module(ch3).-behaviour(gen_server).-export([start_link/0]).-export([alloc/0, free/1]).-export([init/1, handle_call/3, handle_cast/2]).start_link() -> gen_server:start_link({local, ch3}, ch3, [], []).alloc() -> gen_server:call(ch3, alloc).free(Ch) -> gen_server:cast(ch3, {free, Ch}).init(_Args) -> {ok, channels()}.handle_call(alloc, _From, Chs) -> {Ch, Chs2} = alloc(Chs), {reply, Ch, Chs2}.handle_cast({free, Ch}, Chs) -> Chs2 = free(Ch, Chs), {noreply, Chs2}.
This code will be explained in the next section.
Start a gen_server
In the previous section, you can usech3:start_link()To start this gen_server:
start_link() -> gen_server:start_link({local, ch3}, ch3, [], []) => {ok, Pid}
start_linkFunction calledgen_server:start_link/4. This function generates a new process where a gen_server is connected in parallel.
- First Parameter
{local, ch3}The name is specified. In this case, gen_srever will be registered locallych3. If the name is ignored, gen_server will not be registered, and its PID must be used. The name can also be{global, Name}In this case, gen_server usesglobal:register_name/2.
- The second parameter,
ch3Is the name of the callback module, that is, the module where the callback function is placed. Here, the interface function (start_link,allocAndfree) And callback functions (init,handle_callAndhandle_cast). In general, this is a good programming practice. It indicates that the code of the same process is included in the same module.
- The third parameter, [], will be passed to the callback function
init. Here,initThis parameter is ignored without any input data.
- The fourth parameter, [], is the list of parameters. For specific parameters, see
gen_server(3).
After the name is successfully registered, the new gen_server process will call the callback function.ch3:init([])..initReturn{ok, State}, WhereStateIs the internal status of gen_server. Here, the status is available channels.
init(_Args) -> {ok, channels()}.
Note:gen_server:start_linkIs synchronized. It is returned only when gen_server is fully initialized and ready to accept the request.
If gen_server is part of a supervision tree, that is, gen_server is initiated by a supervisor, you must usegen_server:start_link. There is another functiongen_server:startIt is used to start an independent gen_server, that is, a gen_server that is not part of a supervision tree.
Synchronous Call -- call
Synchronous requestalloc()Yesgen_server:call/2Implemented:
alloc() -> gen_server:call(ch3, alloc).
ch3Is the name of gen_server, which must be the same as the name at startup.allocIs the actual request.
The request is sent to the gen_server in the form of a message. After receiving the request, gen_server callshandle_call(Request, From, State), It should return a tuples{reply, Reply, State1}.ReplyIs the response that needs to be returned to the client.State1Is a new value of gen_server status.
handle_call(alloc, _From, Chs) -> {Ch, Chs2} = alloc(Chs), {reply, Ch, Chs2}.
Here, the response is the channel allocated.ChGen_server then waits for a new request and now maintains a list of the latest available channels.
Asynchronous request -- cast
Asynchronous requestfree(ch)Usegen_server:cast/2Implementation:
free(Ch) -> gen_server:cast(ch3, {free, Chr}).
ch3Is the name of gen_server.{free, Ch}Is the actual request.
The request is sent tocast, Which callsfreeAnd then returnsok.
When gen_server receives the request, it callshandle_cast(Request, Stats), Returns a tuples.{noreply, State1}.State1Is a new value of gen_server status.
handle_cast({free, Ch}, Chs) -> Chs2 = free(Ch, Chs), {noreply, Chs2}.
Here, the new status is the list of available channels updatedChs2. Gen_server can now accept new requests.
Stop in the supervision tree
If gen_server is part of a supervision tree, you do not need to stop the function. Its supervisor will automatically terminate it. It is set byClose PolicyDefinition.
If some cleanup is required before termination, the closing policy must be a timeout value and gen_server must be ininitFunction to capture the exit signal. When gen_server is required to be disabled, it will call the callback function.terminate(shutdown, State):
init(Args) -> ..., process_flag(trap_exit, true), ..., {ok, State}....terminate(shutdown, State) -> ..code for cleaning up here.. ok.Standalone gen_server
If gen_server is not part of a supervision tree, you can use a stop function, for example:
...export([stop/0])....stop() -> gen_server:cast(ch3, stop)....handle_cast(stop, State) -> {stop, normal, State};handle_cast({free, Ch}, State) -> .......terminate(normal, State) -> ok.
Callback Function ProcessingstopRequest and return a tuple{stop, normal, State1}, WherenormalIt indicates that this is a normal termination,State1Is a new value of gen_server status. This will trigger gen_server callsterminate(normal, State1)To end elegantly.
Process other messages
To enable gen_server to process messages other than requests, you must implement a callback function.handle_info(Info, State)To process them. For example, if gen_server connects to another process (non-governor) and captures the exit signal, other messages will have an exit message.
handle_info({‘EXIT‘, Pid, Reason}, State) -> ..code to handle exits here.. {noreply, State1}.
2. gen_server Behavior