수색…


비고

gen_server 는 Erlang의 중요한 기능 gen_server 기능의 모든 측면을 이해하기위한 몇 가지 전제 조건이 필요합니다.

Erlang의 기능에 대해 더 많이 배우는 좋은 방법은 공식 github 저장소 에서 소스 코드를 직접 읽는 것입니다. gen_server 동작은 많은 유용한 정보와 흥미로운 구조를 코어에 포함시킵니다.

gen_server 에 정의되어 gen_server.erl 및 관련 문서에서 찾을 수 있습니다 stdlib 얼랑 문서 . gen_server 는 OTP 기능이며 자세한 정보는 OTP 설계 원칙 및 사용자 안내서 에서도 찾을 수 있습니다.

Frank Hebert는 무료 온라인 서적 인 gen_server 대한 좋은 소개를 제공합니다. Erlang You Learn You Some Erlang for great good!

gen_server 콜백에 대한 공식 문서 :

Greeter 서비스

다음은 주어진 이름으로 사람들을 접하게하고 얼마나 많은 사용자가 발생했는지 추적하는 서비스의 예입니다. 아래 사용법을 참조하십시오.

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

초기화 / 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 )이고 두 번째 인수는 요청의 원본 ( _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 )이고 두 번째 인수는 실행중인 gen_server 동작의 현재 상태입니다.

기본적으로이 함수는 호출자에게 데이터를 전달할 수 없으므로 현재 실행을 계속 선택하십시오.

  • {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 가 호출됩니다.

code_change / 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 얼랑 자료 구조를. 첫째, 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