サーチ…


備考

gen_serverはErlangの重要な機能であり、この機能のあらゆる側面を理解するためにいくつかの前提条件が必要です。

Erlangの機能についてもっと知るには、 公式のgithubリポジトリからソースコードを直接読むことができますgen_server振る舞いは、役に立つ情報と興味深い構造をたくさん埋め込んでいます。

gen_serverで定義されてgen_server.erlとその関連ドキュメントはで見つけることができstdlib Erlangのドキュメントgen_serverはOTP機能であり、詳細はOTP設計原則とユーザーズガイドにも記載されています。

Frank Hebertは、無料オンラインの本であるgen_serverもうひとつの優れた紹介をgen_serverしています

gen_serverコールバックの公式ドキュメント:

グリーターサービス

ここでは、特定の名前で人を迎え、遭遇したユーザーの数を追跡するサービスの例を示します。以下の使用方法を参照してください。

%% 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}.

以下は、erlangシェルでのこのサービスの使用例です:

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}

gen_serverビヘイビアの使用

gen_serverは、サーバのように動作する特定の有限状態マシンです。 gen_serverは異なるタイプのイベントを処理できます:

  • handle_call同期要求
  • handle_cast非同期要求
  • handle_info持つ他のメッセージ(OTP仕様で定義されていない)

同期メッセージと非同期メッセージはOTPで指定され、任意の種類のデータを持つ単純なタグ付きタプルです。

単純なgen_serverは次のように定義されています:

-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.

このコードは単純です:受け取ったすべてのメッセージは標準出力に出力されます。

gen_serverの動作

gen_serverを定義するには、ソースコード内で-behaviour(gen_server)を使用して明示的に宣言する必要があります。 behaviourは、米国(行動)または英国(行動)で書くことができます。

この関数は、別の関数を呼び出す簡単なショートカットです: gen_server:start_link/3,4

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

この関数は、 supervisorまたは他のプロセスにリンクされたサーバーを開始するときに呼び出されます。 start_link/3,4はプロセスを自動的に登録することができます(プロセスが一意である必要があると思われる場合)。あるいは単純なプロセスのように単純にスポーンすることもできます。この関数を呼び出すと、 init/1実行しinit/1

この関数はこれらの定義値を返すことができます:

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

init / 1

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

init/1は、サーバーの起動時に最初に実行される関数です。これは、アプリケーションのすべての前提条件を初期化し、新しく作成されたプロセスに状態を戻します。

この関数は、次の定義済みの値のみを返すことができます。

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

State変数は、すべてのもの(例えば、リスト、タプル、プロリスト、マップ、レコード)であり、生成されたプロセス内のすべての関数にアクセス可能なままです。

handle_call / 3

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

gen_server:call/2このコールバックを実行します。最初の引数はメッセージ( _Request )、2番目はリクエストの起点( _From )、最後は実行中のgen_serverビヘイビアの現在の状態( State )です。

呼び出し側に返信したい場合、 handle_call/3は次のいずれかのデータ構造体を返す必要があります:

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

呼び出し元に返信しない場合、 handle_call/3は次のいずれかのデータ構造体を返す必要があります。

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

現在のgen_serverの現在の実行を停止したい場合、 handle_call/3は次のいずれかのデータ構造体を返す必要があります:

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

handle_cast / 2

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

gen_server:cast/2このコールバックを実行します。最初の引数はメッセージ( _Msg )で、2番目の引数は実行中のgen_server動作の現在の状態です。

デフォルトでは、この関数は呼び出し元にデータを渡すことができないため、現在実行を続行する選択肢が2つしかありません。

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

または、現在のgen_serverプロセスを停止します。

  • {stop,Reason,NewState}

handle_info / 2

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

handle_info/2は、非標準OTPメッセージが外部から来たときに実行されます。これは返信することができず、 handle_cast/2ように2つのアクションしか実行できず、現在の実行を継続します。

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

または、現在実行中のgen_serverプロセスを停止します。

  • {stop,Reason,NewState}

終了する/ 2

terminate(_Reason, _State) ->
    ok.

エラーが発生したとき、またはgen_serverプロセスをシャットダウンするときにterminate/2が呼び出されます。

コード変更/ 3

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

code_change/3関数は、実行中のコードをアップグレードするときに呼び出されます。

この関数は、次の定義済みの値のみを返すことができます。

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

このプロセスの開始

あなたのコードをコンパイルして、 simple_gen_serverを起動することができます:

simple_gen_server:start_link().

サーバーにメッセージを送信する場合は、次の機能を使用できます。

% 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).

シンプルなキー/バリューデータベース

このソースコードは、 map Erlangデータ構造に基づいた単純なキー/値ストアサービスを作成しmap 。まず、 gen_serverに関するすべての情報を定義する必要があります。

-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}.

キャッシュサーバーの使用

コードをコンパイルして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
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow