Простая система чата через веб-сокеты с функцией переподключения

Я видел много примеров систем чатов через websocket, реализованных с помощью erlang и cowboy.

В большинстве примеров, которые я видел, используется gproc. На практике каждый обработчик веб-сокетов регистрируется в gproc, а затем передает / получает сообщения от него.

Поскольку пользователь может случайно закрыть веб-страницу, я думаю о подключении к обработчику веб-сокета gen_fsm, который фактически передает / получает все сообщения от gproc. Таким образом, gen_fsm может переключаться из состояния «подключен» в состояние «отключен» всякий раз, когда пользователь выходит из системы, и все сообщения буферизируются. Через некоторое время, если пользователь не снова в сети, gen_fsm завершит свою работу.

Это хорошее решение? Как я могу заставить новый обработчик websocket восстанавливать процесс gen_fsm? Должен ли я регистрировать gen_fsm, используя имя пользователя, или есть лучшее решение?


person user601836    schedule 03.08.2013    source источник
comment
Предлагаю взглянуть на этот довольно новый продукт, erlang- solutions.com/products/, возможно, это поможет   -  person Khashayar    schedule 05.08.2013
comment
Почему бы не реализовать очередь обмена сообщениями, например RabbitMQ или ZeroMQ? ›   -  person Jack Daniel's    schedule 05.09.2013


Ответы (2)


Я делаю следующее:

Когда пользователь подключается к сайту, я меняю gen_server, представляющий пользователя. Затем генеральный сервер регистрируется в gproc как {n, l, {user, UserName}}. (Он может регистрировать такие свойства, как {p, l, {chat, ChannelID}} для прослушивания каналов чата. (См. gproc pub / sub))

Итак, теперь соединение пользователя с веб-сокетом запускает обработчик ковбоя (я использую Bullet). Обработчики запрашивают у gproc pid () gen_server пользователя и регистрируются как получатель сообщений. Итак, теперь, когда пользователь gen_server получает сообщения, он перенаправляет их обработчику веб-сокета.

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

Смотрите: (не проверено)

-module(user_chat).

-record(state, {mailbox,receiver=undefined}).

-export([start_link/1,set_receiver/1,unset_receiver/1]).
%% API

start_link(UserID) ->
    gen_server:start_link(?MODULE,[UserID],[]).

set_receiver(UserID) ->
    set_receiver(UserID,self()).

unset_receiver(UserID) ->
    %% Just set the receiver to undefined
    set_receiver(UserID,undefined).

set_receiver(UserID, ReceiverPid) ->
    UserPid = gproc:where({n,l,UserID}),
    gen_server:call(UserPid,{set_receiver,ReceiverPid}).


%% Gen server internals

init([UserID]) ->
    gproc:reg({n,l,{user,UserID}}),
    {ok,#state{mailbox=[]}}.

handle_call({set_receiver,ReceiverPid},_From,#state{mailbox=MB}=State) ->
    NewMB = check_send(MB,State),
    {reply,ok,State#state{receiver=ReceiverPid,mailbox=NewMB}}.

handle_info({chat_msg,Message},#state{mailbox=MB}=State) ->
    NewMB = check_send([Message|MB],State),
    {noreply, State#state{mailbox=NewMB}}.

%% Mailbox empty
check_send([],_) -> [];
%% Receiver undefined, keep messages
check_send(Mailbox,#state{receiver=undefined}) -> Mailbox
%% Receiver is a pid
check_send(Mailbox,#state{receiver=Receiver}) when is_pid(Receiver) ->
    %% Send all messages
    Receiver ! {chat_messages,Mailbox},
    %% Then return empty mailbox
    [].
person lud    schedule 05.08.2013
comment
рад, что мой ответ порадовал вас. Я забыл таймауты в коде. Вы должны добавить тайм-аут как дополнительный элемент в кортежах {reply | noreply ...}. Лучше всего определить макрос TIMEOUT. Затем вы handle_info (тайм-аут, состояние) и делаете то, что хотите - person lud; 05.08.2013
comment
С помощью этого решения вы не можете отправить сообщение чата кому-то, чей gen_server достиг своего тайм-аута, что-то вроде того, что я позвоню вам в 9 вечера. Итак, пользователям нужен мобильный телефон для синхронизации: o) - person Pascal; 06.08.2013
comment
Да, полностью потому, что это чат, а не электронная почта / SMS / и т.д. Но если вам нужен полноценный IM-сервер, просто введите ejabberd - person lud; 06.08.2013

С помощью предлагаемого вами решения у вас может быть много незавершенных процессов, и вам придется написать «очиститель процессов» для всех пользователей, которые никогда не вернутся. В любом случае он не будет поддерживать выключение виртуальной машины чат-сервера, все сообщения, хранящиеся в живом FSM, исчезнут, если узел выйдет из строя.

Я думаю, что лучший способ - хранить все сообщения в базе данных, такой как mnesia, с отправителем, получателем, датой истечения срока действия ... и проверять любое сохраненное сообщение при подключении, а также иметь процесс очистки сообщений, чтобы уничтожить все просроченные сообщения со временем ко времени.

person Pascal    schedule 04.08.2013
comment
На самом деле я думал об автоматическом завершении каждого gen_fsm после некоторого таймаута. Еще раз спасибо за ваш ответ, но прежде чем принять, я подожду других возможных ответов - person user601836; 04.08.2013