具有重新连接功能的websockets上的简单聊天系统

时间:2013-08-03 14:20:32

标签: erlang websocket cowboy

我见过很多关于使用erlang和cowboy实现的websocket的聊天室系统的例子。

我见过的大多数例子都使用gproc。实际上,每个websocket处理程序都使用gproc注册自己,然后从中广播/接收消息。

由于用户可能意外关闭我正在考虑连接到websocket处理程序的网页gen_fsm,它实际上广播/接收来自gproc的所有消息。这样,只要用户退出并仍然缓冲所有消息,gen_fsm就可以从“连接”状态切换到“断开”状态。如果用户没有重新上线,一段时间后gen_fsm将终止。

这是一个很好的解决方案吗?如何使新的websocket处理程序恢复gen_fsm进程?我应该使用用户名注册gen_fsm还是有更好的解决方案?

2 个答案:

答案 0 :(得分:3)

我的所作所为:

当用户连接到该站点时,我swpawn genrenserver表示用户。然后,gen服务器将自己在gproc中注册为{n,l,{user,UserName}}。 (它可以注册{p,l,{chat,ChannelID}}等属性来收听聊天频道。(见gproc pub/sub))

好的,现在用户websocket连接启动了牛仔处理程序(我使用Bullet)。处理程序向gproc询问用户的gen_server的pid(),并将注册表本身作为消息的接收者。所以现在,当用户gen_server接收消息时,它会将它们重定向到websocket处理程序。

当websocket连接结束时,处理程序从用户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
    [].

答案 1 :(得分:1)

根据您提出的解决方案,您可能有许多待处理的流程,您将不得不为所有永不回来的用户编写“流程清理程序”。无论如何它不支持关闭聊天服务器VM,如果节点关闭,存储在活FSM中的所有消息都将消失。

我认为更好的方法应该是将所有邮件存储在像mnesia这样的数据库中,包括发件人,收件人,到期日期......并检查连接时是否存在任何已存储的邮件,并使用消息清理程序来销毁所有已过期的邮件消息不时。