我见过很多关于使用erlang和cowboy实现的websocket的聊天室系统的例子。
我见过的大多数例子都使用gproc。实际上,每个websocket处理程序都使用gproc注册自己,然后从中广播/接收消息。
由于用户可能意外关闭我正在考虑连接到websocket处理程序的网页gen_fsm,它实际上广播/接收来自gproc的所有消息。这样,只要用户退出并仍然缓冲所有消息,gen_fsm就可以从“连接”状态切换到“断开”状态。如果用户没有重新上线,一段时间后gen_fsm将终止。
这是一个很好的解决方案吗?如何使新的websocket处理程序恢复gen_fsm进程?我应该使用用户名注册gen_fsm还是有更好的解决方案?
答案 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这样的数据库中,包括发件人,收件人,到期日期......并检查连接时是否存在任何已存储的邮件,并使用消息清理程序来销毁所有已过期的邮件消息不时。