Suche…


Bemerkungen

gen_server ist eine wichtige Funktion von Erlang und erfordert einige Voraussetzungen, um alle Aspekte dieser Funktionalität zu verstehen:

Eine gute Möglichkeit, mehr über eine Funktion in Erlang zu erfahren, besteht darin, den Quellcode direkt aus dem offiziellen Github-Repository zu lesen. gen_server Verhalten von gen_server viele nützliche Informationen und eine interessante Struktur in seinem Kern.

gen_server ist in gen_server.erl definiert und die zugehörige Dokumentation finden Sie in der Dokumentation zu stdlib Erlang . gen_server ist eine OTP-Funktion. Weitere Informationen finden Sie auch in den OTP Design Principles und im Benutzerhandbuch .

Frank Hebert gibt Ihnen auch eine weitere gute Einführung in gen_server aus seinem kostenlosen Online-Buch Learn You Some Erlang.

Offizielle Dokumentation für gen_server Rückruf von gen_server :

Greeter Service

Hier ist ein Beispiel für einen Dienst, der die Personen mit dem angegebenen Namen begrüßt und die Anzahl der Benutzer feststellt. Siehe Verwendung unten.

%% greeter.erl
%% Greets people and counts number of times it did so.
-module(greeter).
-behaviour(gen_server).
%% Export API Functions
-export([start_link/0, greet/1, get_count/0]).
%% Required gen server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {count::integer()}).

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

greet(Name) ->
    gen_server:cast(?MODULE, {greet, Name}).

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

%% Private
init({}) ->
    {ok, #state{count=0}}.

handle_cast({greet, Name}, #state{count = Count} = State) ->
    io:format("Greetings ~s!~n", [Name]),
    {noreply, State#state{count = Count + 1}};

handle_cast(Msg, State) ->
    error_logger:warning_msg("Bad message: ~p~n", [Msg]),
    {noreply, State}.

handle_call({get_count}, _From, State) ->
    {reply, {ok, State#state.count}, State};

handle_call(Request, _From, State) ->
    error_logger:warning_msg("Bad message: ~p~n", [Request]),
    {reply, {error, unknown_call}, State}.

%% Other gen_server callbacks
handle_info(Info, State) ->
    error_logger:warning_msg("Bad message: ~p~n", [Info]),
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

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

Hier ein Beispiel für die Verwendung dieses Dienstes in der erlang-Shell:

1> c(greeter).
{ok,greeter}
2> greeter:start_link().
{ok,<0.62.0>}
3> greeter:greet("Andriy").
Greetings Andriy!
ok
4> greeter:greet("Mike").
Greetings Mike!
ok
5> greeter:get_count().
{ok,2}

Verwenden des gen_server-Verhaltens

Ein gen_server ist ein bestimmter endlicher Rechner, der wie ein Server arbeitet. gen_server kann verschiedene Arten von Ereignissen verarbeiten:

  • synchrone Anfrage mit handle_call
  • asynchrone Anforderung mit handle_cast
  • andere Nachricht (nicht in der OTP-Spezifikation definiert) mit handle_info

Synchrone und asynchrone Nachrichten werden in OTP angegeben und sind einfache markierte Tupel mit beliebigen Daten.

Ein einfacher gen_server ist wie folgt definiert:

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

start_link() ->
    Return = gen_server:start_link({local, ?MODULE}, ?MODULE, [], []),
    io:format("start_link: ~p~n", [Return]),
    Return.

init([]) ->
    State = [],
    Return = {ok, State},
    io:format("init: ~p~n", [State]),
    Return.

handle_call(_Request, _From, State) ->
    Reply = ok,
    Return = {reply, Reply, State},
    io:format("handle_call: ~p~n", [Return]),
    Return.

handle_cast(_Msg, State) ->
    Return = {noreply, State},
    io:format("handle_cast: ~p~n", [Return]),
    Return.

handle_info(_Info, State) ->
    Return = {noreply, State},
    io:format("handle_info: ~p~n", [Return]),
    Return.

terminate(_Reason, _State) ->
    Return = ok,
    io:format("terminate: ~p~n", [Return]),
    ok.

code_change(_OldVsn, State, _Extra) ->
    Return = {ok, State},
    io:format("code_change: ~p~n", [Return]),
    Return.

Dieser Code ist einfach: Jede empfangene Nachricht wird in der Standardausgabe gedruckt.

gen_server verhalten

Um einen gen_server zu definieren, müssen Sie ihn explizit in Ihrem Quellcode mit -behaviour(gen_server) . Beachten Sie, dass behaviour in den USA (Verhalten) oder UK (Verhalten) geschrieben werden kann.

Diese Funktion ist eine einfache Verknüpfung, um eine andere Funktion gen_server:start_link/3,4 .

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

Diese Funktion wird aufgerufen, wenn Sie Ihren Server mit einem supervisor oder einem anderen Prozess verknüpfen möchten. start_link/3,4 kann Ihren Prozess automatisch registrieren (wenn Sie der Meinung sind, dass Ihr Prozess eindeutig sein muss) oder einfach wie ein einfacher Prozess erzeugen. Beim Aufruf führt diese Funktion init/1 .

Diese Funktion kann diese definierten Werte zurückgeben:

  • {ok,Pid}
  • ignore
  • {error,Error}

init / 1

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

init/1 ist die erste ausgeführte Funktion, wenn Ihr Server gestartet wird. Hiermit werden alle Voraussetzungen Ihrer Anwendung initialisiert und der Status wird an den neu erstellten Prozess zurückgegeben.

Diese Funktion kann nur die definierten Werte zurückgeben:

  • {ok,State}
  • {ok,State,Timeout}
  • {ok,State,hibernate}
  • {stop,Reason}
  • ignore

State Variable kann alles sein, (zB Liste, Tupel, Maker proplists, Karte, Rekord) und zugänglich bleibt für alle innerhalb erzeugten Prozess - Funktion.

handle_call / 3

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

gen_server:call/2 diesen Callback aus. Das erste Argument ist Ihre Nachricht ( _Request ), das zweite ist der Ursprung der Anforderung ( _From ) und das letzte ist der aktuelle Status ( State ) Ihres laufenden gen_server-Verhaltens.

Wenn Sie dem Anrufer eine Antwort handle_call/3 möchten, muss handle_call/3 eine der folgenden Datenstrukturen zurückgeben:

  • {reply,Reply,NewState}
  • {reply,Reply,NewState,Timeout}
  • {reply,Reply,NewState,hibernate}

Wenn Sie keine Antwort auf den Aufrufer handle_call/3 , muss handle_call/3 eine der folgenden Datenstrukturen zurückgeben:

  • {noreply,NewState}
  • {noreply,NewState,Timeout}
  • {noreply,NewState,hibernate}

Wenn Sie die aktuelle Ausführung Ihres aktuellen gen_servers handle_call/3 möchten, muss handle_call/3 eine der folgenden Datenstrukturen zurückgeben:

  • {stop,Reason,Reply,NewState}
  • {stop,Reason,NewState}

handle_cast / 2

handle_cast(_Msg, State) ->
    {noreply, State}.

gen_server:cast/2 diesen callback ausführen. Das erste Argument ist Ihre Nachricht ( _Msg ) und das zweite der aktuelle Status Ihres laufenden gen_server-Verhaltens.

Standardmäßig kann diese Funktion keine Daten an den Anrufer senden. Sie haben also nur zwei Möglichkeiten, die aktuelle Ausführung fortzusetzen:

  • {noreply,NewState}
  • {noreply,NewState,Timeout}
  • {noreply,NewState,hibernate}

Oder stoppen Sie Ihren aktuellen gen_server Prozess:

  • {stop,Reason,NewState}

handle_info / 2

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

handle_info/2 wird ausgeführt, wenn eine nicht standardmäßige OTP-Nachricht von außerhalb der Welt kommt. Dieser kann nicht antworten und kann, wie handle_cast/2 , nur zwei Aktionen ausführen und die aktuelle Ausführung fortsetzen:

  • {noreply,NewState}
  • {noreply,NewState,Timeout}
  • {noreply,NewState,hibernate}

Oder stoppen Sie den aktuell laufenden gen_server Prozess:

  • {stop,Reason,NewState}

beenden / 2

terminate(_Reason, _State) ->
    ok.

terminate/2 wird aufgerufen, wenn ein Fehler auftritt oder Sie Ihren gen_server Prozess herunterfahren gen_server .

code_change / 3

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

code_change/3 Funktion wird aufgerufen, wenn Sie Ihren laufenden Code aktualisieren möchten.

Diese Funktion kann nur die definierten Werte zurückgeben:

  • {ok, NewState}
  • {error, Reason}

Diesen Prozess starten

Sie können Ihren Code kompilieren und simple_gen_server starten:

simple_gen_server:start_link().

Wenn Sie eine Nachricht an Ihren Server senden möchten, können Sie diese Funktionen verwenden:

% will use handle_call as callback and print:
%   handle_call: mymessage
gen_server:call(simple_gen_server, mymessage).

% will use handle_cast as callback and print:
%   handle_cast: mymessage
gen_server:cast(simple_gen_server, mymessage).

% will use handle_info as callback and print:
%   handle_info: mymessage
erlang:send(whereis(simple_gen_server), mymessage).

Einfache Schlüssel- / Wertedatenbank

Dieser Quellcode erstellt einen einfachen Schlüssel- / Wertspeicherdienst basierend auf der map Erlang-Datenstruktur. Zuerst müssen wir alle Informationen definieren, die unseren gen_server betreffen:

-module(cache).
-behaviour(gen_server).

% our API
-export([start_link/0]).
-export([get/1, put/2, state/0, delete/1, stop/0]).

% our handlers
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

% Defining our function to start `cache` process:

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

%%%%%%%%%%%%%%
% API

% Key/Value database is a simple store, value indexed by an unique key. 
% This implementation is based on map, this datastructure is like hash 
# in Perl or dictionaries in Python. 

% put/2
% put a value indexed by a key. We assume the link is stable 
% and the data will be written, so, we use an asynchronous call with 
% gen_server:cast/2.

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

% get/1
% take one argument, a key and will a return the value indexed 
% by this same key. We use a synchronous call with gen_server:call/2.

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

% delete/1 
% like `put/1`, we assume the data will be removed. So, we use an 
% asynchronous call with gen_server:cast/2.

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

% state/0 
% This function will return the current state (here the map who contain all 
% indexed values), we need a synchronous call.

state() ->
    gen_server:call(?MODULE, {get_state}).

% stop/0
% This function stop cache server process.

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

%%%%%%%%%%%%%%%
% Handlers

% init/1
% Here init/1 will initialize state with simple empty map datastructure.

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

% handle_call/3
% Now, we need to define our handle. In a cache server we need to get our 
% value from a key, this feature need to be synchronous, so, using 
% handle_call seems a good choice:

handle_call({get, Key}, From, State) ->
    Response = maps:get(Key, State, undefined),
    {reply, Response, State};

% We need to check our current state, like get_fea

handle_call({get_state}, From, State) ->
    Response = {current_state, State},
    {reply, Response, State};

% All other messages will be dropped here.

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

% handle_cast/2
% put/2 will execute this function.

handle_cast({put, {Key, Value}}, State) ->
    NewState = maps:put(Key, Value, State),
    {noreply, NewState};

% delete/1 will execute this function.

handle_cast({delete, Key}, State) ->
    NewState = maps:remove(Key, State),
    {noreply, NewState};

% All other messages are dropped here.

handle_cast(_Msg, State) ->
    {noreply, State}.

%%%%%%%%%%%%%%%%
% other handlers

% We don't need other features, other handlers do nothing.

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

terminate(_Reason, _State) ->
    ok.

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

Verwendung unseres Cache-Servers

Wir können jetzt unseren Code kompilieren und ihn mit erl .

% compile cache
c(cache).

% starting cache server
cache:start_link().

% get current store
% will return:
%   #{}
cache:state().

% put some data
cache:put(1, one).
cache:put(hello, bonjour).
cache:put(list, []).

% get current store
% will return:
%   #{1 => one,hello => bonjour,list => []}
cache:state().

% delete a value
cache:delete(1).
cache:state().
%   #{1 => one,hello => bonjour,list => []}

% stopping cache server
cache:stop().


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow