Erlang gen_server с долгосрочными задачами - PullRequest
7 голосов
/ 16 января 2012

Добрый день,

У меня есть процесс gen_server, который периодически выполняет некоторые длительные задачи обновления состояния в handle_info:

handle_info(trigger, State) ->
    NewState = some_long_running_task(),
    erlang:send_after(?LOOP_TIME, self(), trigger),
    {noreply, NewState}.

Но когда такая задача выполняется,тогда весь сервер перестает отвечать на запросы и любой вызов приводит к краху всего сервера:

my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
     in function  gen_server:call/2

Как можно избежать блокировки gen_server?И когда один вызов my_gen_server:status() в любое время, результат должен быть примерно таким: {ok, task_active}

Ответы [ 2 ]

13 голосов
/ 16 января 2012

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

Пусть gen_server связан с процессом, выполняющим эту долгосрочную задачу, и пусть gen_server знает PID или зарегистрированное имя, чтобы в случае сигналов выхода он мог изолировать смерть этого важного процесса от остальных.

handle_info(trigger, State) ->
    Pid = spawn_link(?MODULE,some_long_running_task,[State]),
    NewState = save_pid(Pid,State),
    {noreply, NewState};
handle_info({'EXIT',SomePid,_},State)->
    case lookup_pid(State) == SomePid of
        false -> %% some other process
            {noreply,State};
        true ->
            %% our process has died
            %% what do we do now ?
            %% spawn another one ?
            %% thats your decision to take
            ....
            ....
            {noreply,State}
    end;
handle_info({finished,TaskResult},State)->
    .....%% update state e.t.c.
    erlang:send_after(?LOOP_TIME, self(), trigger),
    {noreply,NewState}.

some_long_running_task(ServerState)->
    ....do work
    ....return results
5 голосов
/ 16 января 2012

Этот вызов не приводит к сбою, а просто к исключению, которое может быть перехвачено:

status() ->
  try gen_server:call(my_gen_server, status)
  catch
    exit:{timeout,_} -> {ok, task_active}
  end.

Однако вызов останется в очереди сервера, и послеон заканчивает обработку текущего сообщения, отправляет ответное сообщение: {ServerRef, Reply}, которое должно быть отброшено вызывающим процессом.

Единственный способ избежать блокировки любого процесса в Erlang (будь то gen_server илинет) не запускать блокирующие задачи на нем.Таким образом, другой альтернативой может быть запуск ваших длинных задач в другом процессе, который взаимодействует только с вашим сервером, так что никого не волнует, что он заблокирован.

...