Асинхронная обработка сообщений Erlang с помощью gen_server:cast/2

Я ищу хорошие примеры асинхронной обработки сообщений Erlang с помощью gen_server:cast/2.

Я видел пример в модуле OTP ssh, который получает запрос через Module:handle_cast/2, держит его в локальной очереди модуля и отправляет обратно ответное сообщение, соответствующее запросу позже, путем явной отправки сообщения. вызывающему абоненту. Когда я попытался прочитать его, я едва мог уследить за кодом и не смог уловить идею.

Фрагмент псевдокода приветствуется.


person jj1bdx    schedule 14.06.2011    source источник
comment
Кажется, это иллюстрирует разницу между cast и call: -асинхронный   -  person Stratus3D    schedule 27.06.2014


Ответы (2)


Я полагаю, вы имеете в виду модуль ssh_connection_manager.

Когда вы выполняете gen_server:cast/2, запрос обрабатывается в функции Module:handle_cast/2. Здесь следует отметить пару вещей:

  • В параметрах handle_cast у вас нет информации об отправителе, поэтому вы не можете - если вы не отправите эту информацию в самом сообщении - отправить ему какой-либо результат.
  • Клиент после выдачи gen_server:cast/2 не будет ждать ответа. На самом деле ему все равно, пришло сообщение или нет (за некоторыми исключениями).
  • В handle_cast/2 вы можете просто вернуть кортеж noreply или stop, поэтому нет возможности вернуть ответ там.

Сказал, что идея кода, на который вы смотрели, должна быть (упрощая вещи):

  • Выполняется первоначальный синхронный gen_server:call/2
  • From клиента передается на сервер и сохраняется в его состоянии (на самом деле кажется, что с этим аргументом создается дополнительный процесс). Этот трюк полезен, когда вы не можете вычислить возвращаемое значение во время handle_call.
  • На этом этапе у вас есть две возможности, в зависимости от того, нужна ли вам дополнительная информация для вычисления результата от других клиентов (A) или от того же клиента (B):

    • A. Return something like {noreply NewState} in the handle_call. This will give you the possibility to handle other requests while the client is still holding. When the result is ready you can send it back to the client using a gen_server:reply().
    • B. Вернуть {ответ, ок, состояние} клиенту. Клиент продолжит выполнение, возможно, выполнив серию cast/2. Затем клиент запросит у вас окончательный результат с новым gen_server:call/2.
person Roberto Aloi    schedule 14.06.2011
comment
Благодарю за разъяснение. Теперь я помню поток кода. Я должен был написать, что ssh сам по себе является поведением. - person jj1bdx; 15.06.2011
comment
когда вы имеете в виду, что клиент все еще держится, вы имеете в виду, что клиент заблокирован и не может продолжить выполнение? Есть ли способ не блокировать? Можно ли получить отложенный ответ? или для этого лучше всего создать собственное поведение, как сказал jj1bdx? - person Ale; 29.06.2011
comment
Я думаю, это может помочь trapexit.org/Building_Non_Blocking_Erlang_apps - person Ale; 29.06.2011

Обычно вы не ожидаете прямого ответа при отправке приведения, иначе вы бы использовали gen_server:call.

Пример из реальной жизни: у меня есть gen_server, который обрабатывает некоторые «каналы», и есть много способов добавить имя канала в журнал ошибок. Имя канала хранится в состоянии gen_server. Чтобы не задерживать процесс, который хочет зарегистрировать ошибку, я не использую синхронный вызов «получить имя», чтобы получить имя перед добавлением, а отправляю сообщения с приведениями:

error(Pid, Tags) ->
    gen_server:cast(Pid, {log, error_report, Tags}).

warning(Pid, Tags) ->
    gen_server:cast(Pid, {log, warning_report, Tags}).

info(Pid, Tags) ->
    gen_server:cast(Pid, {log, info_report, Tags}).

Приведение обрабатывается довольно простым обработчиком, который не возвращает значение.

handle_cast({log, Report, Tags}, #state{name=Name}=State) ->
    error_logger:Report([{chan, Name} | Tags]),
    {noreply, State};

Если у вас есть асинхронные сообщения для отправки обратно, это совершенно не зависит от обработки приведения. Вам как-то нужно знать, куда отправлять эти сообщения, которые вы должны каким-то образом хранить в State, или вы используете фиксированное имя.

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

person Peer Stritzinger    schedule 14.06.2011