我要做的是以下内容:
当用户连接到该站点时,我会生成一个代表该用户的 gen_server。然后,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
[].