Erlang 通过具有重新连接功能的WebSocket的简单聊天系统

Erlang 通过具有重新连接功能的WebSocket的简单聊天系统,erlang,websocket,cowboy,Erlang,Websocket,Cowboy,我看到了许多使用erlang和cowboy在websocket上实现聊天室系统的例子 我看到的大多数示例都使用gproc。实际上,每个websocket处理程序都向gproc注册自己,然后广播/接收来自gproc的消息 由于用户可能会意外关闭网页,我正在考虑连接到websocket处理程序一个gen_fsm,它实际广播/接收来自gproc的所有消息。这样,只要用户退出,gen_fsm就可以从“已连接”状态切换到“已断开”状态,并且仍然可以缓冲所有消息。一段时间后,如果用户未重新联机,gen_fs

我看到了许多使用erlang和cowboy在websocket上实现聊天室系统的例子

我看到的大多数示例都使用gproc。实际上,每个websocket处理程序都向gproc注册自己,然后广播/接收来自gproc的消息

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


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

对于您提出的解决方案,您可能有许多流程等待处理,并且您必须为所有再也不会回来的用户编写“流程清理器”。无论如何,它不支持关闭聊天服务器虚拟机,如果节点关闭,存储在living FSM中的所有消息都将消失


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

使用您提出的解决方案,您可能会有许多进程挂起,您必须为所有永远不会回来的用户编写一个“进程清理器”。无论如何,它不支持关闭聊天服务器虚拟机,如果节点关闭,存储在living FSM中的所有消息都将消失


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

我所做的是以下几点:

当一个用户连接到该站点时,我切换到一个代表该用户的gen_服务器。然后,gen服务器在gproc中将自己注册为{n,l,{user,UserName}。(它可以注册像{p,l,{chat,ChannelID}}这样的属性来收听聊天频道。(请参阅))

现在用户websocket连接启动牛仔处理程序(我使用)。处理程序向gproc请求用户的gen_服务器的pid(),并将自身注册为消息的接收者。现在,当用户gen_服务器接收到消息时,它会将消息重定向到websocket处理程序

当websocket连接结束时,处理程序将从用户gen_服务器注册,因此用户gen_服务器将保留消息,直到下一次连接或下一次超时。在超时时,您可以简单地终止服务器(消息将丢失,但没有问题)

见:(未测试)


我所做的工作如下:

当一个用户连接到该站点时,我切换到一个代表该用户的gen_服务器。然后,gen服务器在gproc中将自己注册为{n,l,{user,UserName}。(它可以注册像{p,l,{chat,ChannelID}}这样的属性来收听聊天频道。(请参阅))

现在用户websocket连接启动牛仔处理程序(我使用)。处理程序向gproc请求用户的gen_服务器的pid(),并将自身注册为消息的接收者。现在,当用户gen_服务器接收到消息时,它会将消息重定向到websocket处理程序

当websocket连接结束时,处理程序将从用户gen_服务器注册,因此用户gen_服务器将保留消息,直到下一次连接或下一次超时。在超时时,您可以简单地终止服务器(消息将丢失,但没有问题)

见:(未测试)


实际上,我在考虑在超时后自动终止每一代fsm。再次感谢您的回答,但在接受之前,我将等待其他可能的回答。实际上,我正在考虑在超时后自动终止每一代fsm。再次感谢您的回答,但在接受之前,我将等待其他可能的回答。我建议看一看这个相当新的产品,也许这会有帮助。为什么不实施RabbitMQ或ZeroMQ之类的消息队列?我建议看一看这个相当新的产品,也许这会有帮助,为什么不实现RabbitMQ或ZeroMQ>之类的消息队列呢?很高兴我的回答让您满意。我在密码里忘了超时。您必须在{reply | noreply…}元组中添加超时作为额外元素。最好是定义一个超时宏。然后你处理你的信息(超时,状态)并做你想做的事情。使用这个解决方案,你不能向某个gen_服务器已经超时的人发送聊天信息,比如“我会在晚上9点给你打电话”。所以用户需要一部手机来同步:o)是的,完全是因为它是聊天,而不是电子邮件/短信/等等。。您可以随时检查gen_服务器是否处于活动状态,如果未处于活动状态,则可以存储消息。但是如果你想要一个完整的即时通讯服务器,那就告诉我,我的回答让你满意。我在密码里忘了超时。您必须在{reply | noreply…}元组中添加超时作为额外元素。最好是定义一个超时宏。然后你处理你的信息(超时,状态)并做你想做的事情。使用这个解决方案,你不能向某个gen_服务器已经超时的人发送聊天信息,比如“我会在晚上9点给你打电话”。所以用户需要一部手机来同步:o)是的,完全是因为它是聊天,而不是电子邮件/短信/等等。。您可以随时检查gen_服务器是否处于活动状态,如果未处于活动状态,则可以存储消息。但是如果你想要一个完整的IM服务器,只需安装ejabberd即可
-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
    [].