Почему модуль обратного вызова Erlang / OTP gen_server должен обеспечивать функцию handle_cast? - PullRequest
0 голосов
/ 04 мая 2018

Я могу понять, почему модуль обратного вызова должен обеспечивать функции init и handle_call. init - для создания начального состояния, а handle_call - основная цель для создания серверного процесса: для обслуживания запросов.

Но я не понимаю, почему handle_cast требуется. Не может ли модуль gen_server обеспечить реализацию по умолчанию, как это делается для многих других обратных вызовов? Это может быть просто как 1009 *

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

Мне кажется, что большинство модулей обратного вызова в любом случае предоставляют такие же noops.

Ответы [ 2 ]

0 голосов
/ 08 мая 2018

и handle_call - основная цель создания серверного процесса: для обслуживания запросов.

Клиент-серверная архитектура может применяться к гораздо более широкому кругу проблем, чем просто веб-сервер, обслуживающий документы. Одним из примеров является частотный сервер, обсуждаемый в нескольких книгах по эрлангу. Клиент может запросить у сервера частоту для совершения телефонного звонка, затем клиент должен дождаться, пока сервер вернет определенную частоту, прежде чем будет сделан вызов. Это классическая gen_server:call() ситуация: клиент должен дождаться, пока сервер вернет частоту, прежде чем клиент сможет сделать телефонный звонок.

Однако, когда клиент настроен на использование частоты, клиент отправляет сообщение на сервер, сообщая серверу об освобождении частоты. В этом случае клиенту не нужно ждать ответа от сервера, потому что клиенту даже не важно, каков ответ сервера. Клиенту просто нужно отправить сообщение освобождения, затем клиент может продолжить выполнение другого кода. Сервер обязан обработать сообщение освобождения, когда у него есть время, а затем переместить частоту из «занятого» списка в «свободный» список, чтобы частота была доступна для использования другими клиентами. В результате клиент использует gen_server:cast() для отправки сообщения об освобождении на сервер.

Теперь, каково "основное назначение" частотного сервера? Выделить или освободить частоты? Если сервер не освобождает частоты, то после определенного числа клиентских запросов больше не будет раздачи частот, и клиенты получат сообщение, что «частоты отсутствуют». Следовательно, для правильной работы системы необходим акт освобождения частот. Другими словами, handle_call() не является «главной целью» сервера - handle_cast() одинаково важен - и оба обработчика необходимы для обеспечения максимально эффективной работы системы.

Не мог модуль gen_server предоставить реализацию по умолчанию, как это делает для многих других обратных вызовов?

Почему вы не можете создать шаблон gen_server, который по умолчанию имеет реализацию handle_cast()? Вот шаблон gen_server по умолчанию для emac:

-behaviour(gen_server).

%% API
-export([start_link/0]).

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

-define(SERVER, ?MODULE).

-record(state, {}).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @spec handle_call(Request, From, State) ->
%%                                   {reply, Reply, State} |
%%                                   {reply, Reply, State, Timeout} |
%%                                   {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, Reply, State} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================
0 голосов
/ 08 мая 2018

handle_cast аналогичен handle_call и используется для асинхронных вызовов к выполняемому вами серверу gen_server (вызовы синхронны). Он обрабатывает запросы для вас, но не с ответом, как call.

Подобно gen_call он может изменить состояние вашего gen_server (или оставить его как есть, в соответствии с вашими потребностями и реализацией). Кроме того, он может остановить ваш сервер, режим гибернации и т. Д., Как и ваши звонки - см. , чтобы узнать некоторые эрланги для примеров и более широкого объяснения.

Как вы сказали в вопросе, это может быть «тупиком», но в некоторых случаях лучше реализовывать и обрабатывать асинхронные вызовы на ваш сервер.

...