и 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
%%%===================================================================