Erlang OTP Programming first experience--gen_server and behavioral patterns

Source: Internet
Author: User

Http://blog.sina.com.cn/s/blog_3fe961ae0101k4p6.html

Behavior patterns are very similar to interfaces in object-oriented languages, at least as I understand them. The OTP behavior pattern divides some recurring patterns into two parts, the generic part and the implementation-specific part of the application, which is similar to the process of abstracting out an interface in object-oriented programming. This article gives an example of the most common behavior pattern in OTP: General server, or Gen_server.

Writing the Gen_server callback module roughly includes a 3-phase step:

(1) Determine the name of the callback module;

(2) Write the interface function (called by the client);

(3) Implement the 6 callback functions of Gen_server (called by the Gen_server container) in the callback module.

Here is an example code in Erlang OTP concurrency programming, which implements a simple RPC service by implementing the Gen_server interface, which allows the client to invoke any function exported from any module on the server side. A get_count query interface is provided to query the number of requests that have been processed by the current server. In addition, Start_link () and stop () are used to stop the server process.

Tr_server.erl

%%%-------------------------------------------------------------------

%%% @author Martin & Eric

%%% [http://www.erlware.org]

%%% @copyright 2008-2010 Erlware

%%% @doc RPC over TCP server. This module defines a server process

%%% listens for incoming TCP connections and allows the user to

%%% execute RPC commands via that TCP stream.

%%% @end

%%%-------------------------------------------------------------------

-module (Tr_server).

-behaviour (Gen_server).

Percent percent API

-export ([

START_LINK/1,

start_link/0,

get_count/0,

stop/0

]).

Gen_server Callbacks

-export ([Init/1, HANDLE_CALL/3, HANDLE_CAST/2, HANDLE_INFO/2,

TERMINATE/2, CODE_CHANGE/3]).

-define (SERVER,? MODULE).

-define (Default_port, 1055).

-record (state, {port, lsock, Request_count = 0}).

%%%===================================================================

%%% API

%%%===================================================================

%%--------------------------------------------------------------------

Percent @doc starts the server.

%%

Percent @spec Start_link (Port::integer ()) {OK, Pid}

Percent of where

Percent PID = pid ()

Percent @end

%%--------------------------------------------------------------------

Start_link (Port),

Gen_server:start_link ({local,? SERVER},? MODULE, [Port], []).

Percent @spec Start_link () {OK, Pid}

Percent @doc Calls ' Start_link (port) ' using the default port.

Start_link ()

Start_link (? Default_port).

%%--------------------------------------------------------------------

Percent @doc fetches the number of requests made to this server.

Percent @spec get_count () {OK, count}

Percent of where

Percent Count = integer ()

Percent @end

%%--------------------------------------------------------------------

Get_count ()

Gen_server:call (? SERVER, get_count).

%%--------------------------------------------------------------------

Percent @doc Stops the server.

Percent @spec Stop (), OK

Percent @end

%%--------------------------------------------------------------------

Stop ()

Gen_server:cast (? SERVER, stop).

%%%===================================================================

%%% Gen_server Callbacks

%%%===================================================================

Init ([Port])

{OK, lsock} = Gen_tcp:listen (Port, [{active, true}]),

{OK, #state {port = port, Lsock = Lsock}, 0}.

Handle_call (get_count, _from, State)

{reply, {OK, state#state.request_count}, state}.

Handle_cast (stop, state),

{Stop, Normal, state}.

Handle_info ({TCP, Socket, rawdata}, state),

Do_rpc (Socket, RawData),

RequestCount = State#state.request_count,

{noreply, State#state{request_count = RequestCount 1}};

Handle_info (Timeout, #state {lsock = lsock} = state)

{OK, _sock} = gen_tcp:accept (Lsock),

{noreply, State}.

Terminate (_reason, _state)

Ok.

Code_change (_OLDVSN, State, _extra)

{OK, state}.

%%%===================================================================

%%% Internal functions

%%%===================================================================

Do_rpc (Socket, RawData)

Try

{M, F, A} = SPLIT_OUT_MFA (RawData),

Result = Apply (M, F, A),

Gen_tcp:send (Socket, Io_lib:fwrite ("~p~n", [Result]))

Catch

_class:err

Gen_tcp:send (Socket, Io_lib:fwrite ("~p~n", [ERR]))

End.

SPLIT_OUT_MFA (RawData)

MFA = Re:replace (RawData, "\r\n$", "" ", [{return, List}]),

{match, [M, F, A]} =

Re:run (MFA,

"(. *):(. *) \s*\\ ((. *) \s*\\) \s*.\s*$",

[{capture, [+], list}, Ungreedy]),

{List_to_atom (M), List_to_atom (F), Args_to_terms (A)}.

Args_to_terms (Rawargs)

{OK, toks, _line} = erl_scan:string ("[" Rawargs "].", 1),

{OK, Args} = Erl_parse:parse_term (Toks),

Args.

The author runs this program in a Linux environment:

1> C (tr_server).

{Ok,tr_server}

2> Tr_server:start_link ().

{Ok,<0.39.0>}

3>

Here you need to start a shell and enter:

[Email protected]:~# telnet 127.0.0.1 1055

Trying 127.0.0.1 ...

Connected to 127.0.0.1.

Escape character is ' ^] '.

Then go back to the Erlang console and enter:

3> Tr_server:get_count ().

{ok,0}

4> tr_server:stop ().

Ok

Why first connect to port 1055 with Telnet? Analyze the behavior of the init ([Port]) function. The init ([Port]) function first establishes a TCP listener socket on the specified port with the GEN_TCP module in the standard library:

{OK, lsock} = Gen_tcp:listen (Port, [{active, true}]),

The init ([Port]) function then returns a ternary group containing the atomic OK, the initial process state, and the number 0:

{OK, #state {port = port, Lsock = Lsock}, 0}.

This 0 indicates a timeout value. Setting the timeout to zero causes the Gen_server container to trigger a timeout immediately after the end of INIT/1, forcing the process to process the timeout message (completed by HANDLE_INFO/2) the first time after the initialization is complete. The purpose of using 0 here is to wake the server and perform some of the specified actions: Wait for the connection on the listener socket to be created. Gen_server will always block here when no connection is received, so if you send a tr_server:get_count () request at this point, you will get a timeout feedback:

* * Exception exit: {Timeout,{gen_server,call,[tr_server,get_count]}}

The RPC service implemented by Tr_server can theoretically call any function exported from any module on the server side. For example, you can enter in Telnet:

Init:stop ().

Return:

Ok

Connection closed by foreign host.

This is because Init:stop () shuts down the entire Erlang node running the RPC server.

Finally, let's look at some of the commonly used callback functions in Gen_server. Open Gen_server Source (on the author's Windows system, this file is located in C:\Program files (x86) \erl5.8.5\lib\stdlib-1.17.5\src), in the header notes of the file, The format of the parameters to be returned by each interface and the execution process of gen_server are described in detail.

%%% ---------------------------------------------------

%%%

%%% The idea behind the user module

%%% provides (different) functions to handle different

%%% kind of inputs.

%%% If The Parent process terminates the MODULE:TERMINATE/2

%%% function is called.

%%%

%%% the user module should export:

%%%

%%% Init (Args)

%%% ==> {OK, state}

%%% {OK, state, Timeout}

%%% Ignore

%%% {stop, Reason}

%%%

%%% Handle_call (MSG, {from, tags}, state)

%%%

%%% ==> {reply, reply, state}

%%% {reply, reply, state, Timeout}

%%% {noreply, State}

%%% {noreply, state, Timeout}

%%% {stop, Reason, Reply, state}

%%% Reason = Normal | Shutdown | term terminate (state) is called

%%%

%%% Handle_cast (MSG, State)

%%%

%%% ==> {noreply, State}

%%% {noreply, state, Timeout}

%%% {Stop, Reason, state}

%%% Reason = Normal | Shutdown | term terminate (state) is called

%%%

%%% Handle_info (info, state) info is e.g. {' EXIT ', P, R}, {Nodedown, N}, ...

%%%

%%% ==> {noreply, State}

%%% {noreply, state, Timeout}

%%% {Stop, Reason, state}

%%% Reason = Normal | Shutdown | term, terminate (state) is called

%%%

%%% Terminate (Reason, state) Let the user module clean up

%%% always called when server terminates

%%%

%%% ==> OK

%%%

%%%

%%% the work flow (of the server) can described as follows:

%%%

%%% User Module Generic

%%%   -----------                          -------

%%% Start-----> Start

%%% Init <-----.

%%%

%%% Loop

%%% Handle_call <-----.

%%%-----> Reply

%%%

%%% Handle_cast <-----.

%%%

%%% Handle_info <-----.

%%%

%%% Terminate <-----.

%%%

%%%-----> Reply

%%%

%%%

%%% ---------------------------------------------------

Reference: Erlang OTP Concurrent Programming combat

Erlang OTP Programming first experience--gen_server and behavioral patterns

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.