cowboy websocket asynchronous reply

40 views
Skip to first unread message

Taras J. Honcharuk

unread,
Mar 10, 2017, 4:36:39 AM3/10/17
to erlang-i...@googlegroups.com
Доброго вемени суток

Попытался сделать асинронную отправку пакетов в cowboy websocket

текст для примера:

-module(ws_handler).

-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).

init(Req, _Opts) ->
        {cowboy_websocket, Req, []}.

websocket_handle({text, Msg}, State) ->
   Reply1 = do_something(Msg),
   self() ! {reply, Reply1},
   Reply2 = do_something(Reply1),
   self() ! {reply, Reply2},
   Reply3 = do_something(Reply2),
   {reply, {text, Reply3}, State}.

websocket_info({reply, Msg}, State) ->
        {reply, {text, Msg}, State}.

В итоге на браузере получаю ответ в таком порядке
Reply3
Reply1
Reply2

Может кто сталкивался с таким приемом?
Как правильно сделать стоб сообщения возвращались в нужном порядке?
Reply1
Reply2
Reply3

Видел совет с использованием gen_event 
но как его прикрутить чтоб gen_event слал сообщения в socket?
у меня по крайней мере не получилось :(

Serhii Kostiushkin

unread,
Mar 10, 2017, 4:43:30 AM3/10/17
to Erlang в Україні
Попробуй так:

-module(ws_handler).


-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).


init
(Req, _Opts) ->
       
{cowboy_websocket, Req, []}.


websocket_handle
({text, Msg}, State) ->

   
Reply1 = do_something(Msg),
   
self() ! {reply, Reply1},
   
Reply2 = do_something(Reply1),
   
self() ! {reply, Reply2},
   
Reply3 = do_something(Reply2),

   
self() ! {reply, Reply3},
   
{ok, Req, State}.



websocket_info
({reply, Msg}, State) ->
       
{reply, {text, Msg}, State}.





пʼятниця, 10 березня 2017 р. 11:36:39 UTC+2 користувач Taras J. Honcharuk написав:

Taras J. Honcharuk

unread,
Mar 10, 2017, 4:53:58 AM3/10/17
to Erlang в Україні
Возможно я не правильно поставил вопрос
нужно не просто сохранить порядок, но и получать ответ в браузере в момент отрпавки ответа
насколько я понял websocket_info не будет обрабатываться пока не отработает websocket_handle
если do_something/1 выполняется длительное время (обращение к БД)
я хотел оттдавать результат порционно, а не ждать выполнения всего запроса

пятница, 10 марта 2017 г., 11:43:30 UTC+2 пользователь Serhii Kostiushkin написал:

zheka_13

unread,
Mar 10, 2017, 5:40:08 AM3/10/17
to erlang-i...@googlegroups.com
А если во время выполнения websocket_handle туда еще прийдет какойе то сообщение ?
Помоему будет каша.

Наверное проще получить сообщение в websocket_handle и передаеть его дальше какому то ген серверу, а он уже пусть сам выдает порциями или как удобно, на его стороне можно синхронный call использовать.

10 марта 2017 г., 11:53 пользователь Taras J. Honcharuk <taras.h...@gmail.com> написал:

--
Ви отримали це повідомлення, оскільки ви підписані на групу "Erlang в Україні" у Групах Google.
Щоб скасувати підписку на цю групу та більше не отримувати повідомлень із неї, надішліть електронний лист на адресу erlang-in-ukraine+unsubscribe@googlegroups.com.
Щоб опублікувати допис у цій групі, надішліть електронний лист на адресу erlang-in-ukraine@googlegroups.com.
Перейдіть до цієї групи за посиланням https://groups.google.com/group/erlang-in-ukraine.

Taras J. Honcharuk

unread,
Mar 10, 2017, 6:02:51 AM3/10/17
to Erlang в Україні
Пока на ум приходит только вариант переделать do_something в виде gen_server:cast/2
чтоб websocket_handle отработала сразу, а Reply1, Reply2, Reply3 ловить через websocket_info

пятница, 10 марта 2017 г., 11:53:58 UTC+2 пользователь Taras J. Honcharuk написал:

Taras J. Honcharuk

unread,
Mar 10, 2017, 6:22:33 AM3/10/17
to Erlang в Україні
Спасибо за ответ
на самом деле у меня  сейчас так и есть
обработка отдается в gen_server:call
результат прогоняется через фильтр и возвращается сокету
но поскольку call синхронный, info не обработется до завершения handle
как вариант использовать cast
а каши не будет так как в ответе есть свой ID по которому сторона браузера пойме что куда

пятница, 10 марта 2017 г., 12:40:08 UTC+2 пользователь Evgen Polivoda написал:
Щоб скасувати підписку на цю групу та більше не отримувати повідомлень із неї, надішліть електронний лист на адресу erlang-in-ukra...@googlegroups.com.
Щоб опублікувати допис у цій групі, надішліть електронний лист на адресу erlang-i...@googlegroups.com.

Serhii Kostiushkin

unread,
Mar 10, 2017, 7:48:17 AM3/10/17
to Erlang в Україні
Да, либо так, либо избавлятся от синхронной обработки совсем. Например, разбив один ws-запрос на три.

пʼятниця, 10 березня 2017 р. 13:02:51 UTC+2 користувач Taras J. Honcharuk написав:

Taras J. Honcharuk

unread,
Mar 10, 2017, 8:08:58 AM3/10/17
to Erlang в Україні
разбить на три - это как?
можно подробнее,
от браузера же один запрос приходит...
это я ему хочу три ответа дать - (создать новое окно, добавить форму для ввода, добавить блок с данными)

пятница, 10 марта 2017 г., 14:48:17 UTC+2 пользователь Serhii Kostiushkin написал:

Serhii Kostiushkin

unread,
Mar 10, 2017, 8:46:20 AM3/10/17
to Erlang в Україні
Если есть возможность изменять протокол общения, то я бы сделал чтобы именно от бразуера приходило 3 запроса. И не было бы нужды в отдельном генсервере.

пʼятниця, 10 березня 2017 р. 15:08:58 UTC+2 користувач Taras J. Honcharuk написав:

Serhii Kostiushkin

unread,
Mar 10, 2017, 9:07:37 AM3/10/17
to Erlang в Україні
%% Есть еще такой вариант чтобы без отдельного генсервера


-module(ws_handler).

-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2]).

init
(Req, _Opts) ->
       
{cowboy_websocket, Req, []}.

websocket_handle
({text, Msg}, State) ->

   
self() ! {do_one, Msg},
   
self() ! {do_one, Msg},
   
self() ! {do_one, Msg},
   
{ok, Req, State}.

websocket_info
({do_one, Msg}, State) ->
   
Reply = do_something(Msg),
   
{reply, {text, Reply}, State};
websocket_info
({do_two, Msg}, State) ->
   
Reply = do_something(Msg),
   
{reply, {text, Reply}, State};
websocket_info
({do_three, Msg}, State) ->
   
Reply = do_something(Msg),
   
{reply, {text, Reply}, State};

websocket_info
({reply, Msg}, State) ->
   
{reply, {text, Msg}, State}.


пʼятниця, 10 березня 2017 р. 15:46:20 UTC+2 користувач Serhii Kostiushkin написав:

Taras J. Honcharuk

unread,
Mar 10, 2017, 9:45:01 AM3/10/17
to erlang-i...@googlegroups.com
Спасибо

10 марта 2017 г., 16:07 пользователь Serhii Kostiushkin <s.kost...@gmail.com> написал:

--
Ви отримали це повідомлення, оскільки ви підписані на тему в групі "Erlang в Україні" у Групах Google.
Щоб скасувати підписку на цю тему, перейдіть за посиланням https://groups.google.com/d/topic/erlang-in-ukraine/jdJP7HHSbwk/unsubscribe.
Щоб скасувати підписку на цю групу та всі її теми, надішліть електронний лист на адресу erlang-in-ukraine+unsubscribe@googlegroups.com.
Щоб опублікувати допис у цій групі, надішліть електронний лист на адресу erlang-in-ukraine@googlegroups.com.

Перейдіть до цієї групи за посиланням https://groups.google.com/group/erlang-in-ukraine.



--
Yours sincerely
Taras

Volodymyr Stolyarchuk

unread,
Mar 11, 2017, 9:04:00 AM3/11/17
to erlang-i...@googlegroups.com
а чому в websocket_handle ви повертаєте Reply3? чому не зробити так?
websocket_handle({text, Msg}, State) ->
   Reply1 = do_something(Msg),
   Reply2 = do_something(Reply1),
   Reply3 = do_something(Reply2),
   self() ! {reply, Reply2},
   self() ! {reply, Reply3},
   {reply, {text, Reply1}, State}.

хоча в цьому випадку я і не розумію навіщо використовувати websocket_info, якщо він просто віддає серверу те, що отримав, а вся робота відбувається синхронно в websocket_handle. я б тоді вже зробив щось типу такого
websocket_handle({text, Msg}, State) ->
   Reply1 = do_something(Msg),
   Reply2 = do_something(Reply1),
   Reply3 = do_something(Reply2),
   Reply = concat_reply(Reply1,Reply2,Reply3),
   {reply, {text, Reply}, State}.



--
Ви отримали це повідомлення, оскільки ви підписані на групу "Erlang в Україні" у Групах Google.
Щоб скасувати підписку на цю групу та більше не отримувати повідомлень із неї, надішліть електронний лист на адресу erlang-in-ukraine+unsubscribe@googlegroups.com.

Taras J. Honcharuk

unread,
Mar 13, 2017, 2:13:29 AM3/13/17
to erlang-i...@googlegroups.com
Дякую за відповідь
Але спарва в тому що це була спроба зробити з синхронного запиту асинхронний
do_something може оброблятися кілка хвилин, а в деяких випадках і годин
а я хотів одразу віддати серверу щось на зразок "Чекайте.. Ваш запит обробляється" або помилку в залежності від того що отримав в Reply1

11 марта 2017 г., 16:03 пользователь Volodymyr Stolyarchuk <volodymyr....@gmail.com> написал:



--
Yours sincerely
Taras

Volodymyr Stolyarchuk

unread,
Mar 13, 2017, 5:50:52 PM3/13/17
to erlang-i...@googlegroups.com
А як ви здаєте, що відбулась помилка?
я так розумію, що в  do_something виконання роботи передається іншому процесові. 
- Якщо це робити через  gen_server:cast тоді синхронно ми не будемо знати чи відбулась помилка бо ми просто послали повідомлення.
- Якщо використовувати gen_server:call не дасть ніякої паралельності

я робив, дещо подібне коли один запит від клієнта ініціює декілька паралельних задач. що правда робив не через вебсокет, а за допопогою loop_handler, але принцип той же. на websocket я б робив якось так:

-module(ws_handler).

-export([init/2]).
-export([websocket_init/1]).
-export([websocket_handle/2]).
-export([websocket_info/2
]).

-record(state, {
  worker
}).

init(Req, Opts) ->
  {cowboy_websocket, Req, Opts}.

websocket_init(State) ->
  {ok, State}.

websocket_handle({text, Msg}, State) ->
  {ok, Pid, Ref} = do_something(Msg),
  {ok, State#state{worker = {Pid, Ref}}}.

websocket_info(timeout, State) ->
  TimeoutReply = get_error_reply(timeout),
  {reply, {text, TimeoutReply}, State};
websocket_info({worker_reply, WorkerPid, WorkerMsg}, #state{worker = {WorkerPid, Ref}} = State) ->
  demonitor(Ref,[flush]),
  {reply, {text, WorkerMsg}, State};
websocket_info({'DOWN', _, process, WorkerPid, Reason}, #state{worker = {WorkerPid, _}} = State) ->
  ErrorReply = get_error_reply(Reason),
  {reply, {text, ErrorReply}, State};
websocket_info(_Info, State) ->
  {ok, State}.

do_something(Msg) ->
  Pid = get_worker_process(),
  Ref = erlang:monitor(process, Pid),
  gen_server:cast(Pid, {do, Msg}), %% or %% Pid ! {do, Msg},
  {ok, Pid, Ref}.



2017-03-13 8:13 GMT+02:00 Taras J. Honcharuk <taras.h...@gmail.com>:
Дякую за відповідь
Але спарва в тому що це була спроба зробити з синхронного запиту асинхронний
do_something може оброблятися кілка хвилин, а в деяких випадках і годин
а я хотів одразу віддати серверу щось на зразок "Чекайте.. Ваш запит обробляється" або помилку в залежності від того що отримав в Reply1

11 марта 2017 г., 16:03 пользователь Volodymyr Stolyarchuk <volodymyr.stolyarchuk@gmail.com> написал:



--
Yours sincerely
Taras

Taras J. Honcharuk

unread,
Mar 17, 2017, 6:43:02 AM3/17/17
to erlang-i...@googlegroups.com
Дякую за відповідь
спробую зробити щось подібне
Reply all
Reply to author
Forward
0 new messages