计时器:apply_interval/4 在用户越轨时停止,并在 ejabberd 中再次返回



我在Linux机器上使用源代码中的ejabberd-17.03。

我使用用户 A 的 jid 从服务器以编程方式创建了一个临时聊天室,并直接向用户 B 发送邀请,他接受并加入聊天室。

我的用例是两个用户 A 和 B 在聊天室中交换消息。如果在 30 秒内没有用户向其他用户发送任何消息,则聊天室会向这两个用户发送随机选择的消息。

我已经实现如下:

start(_Host, _Opts) ->
ejabberd_hooks:add(user_send_packet, _Host, ?MODULE, myMessage, 95).
stop(_Host) ->
ejabberd_hooks:delete(user_send_packet, _Host, ?MODULE, myMessage,95).
depends(_Host, _Opts)->[{?MODULE,soft}].
mod_opt_type(_Option)->
ok.
myMessage({#message{from = From, to = To, body= Body} =Packet, C2SState}) ->
{UserA,UserB}=select_user(Packet),
PacketType=returnPacketType(Packet),
if
(PacketType==normal) ->
dosomething(),
{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
ets:insert(ref_table, {Key, Ref_or_Reason});
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end;
(PacketType==groupchat)->
do_something_else(),
{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
replace_old_ref_with_new(Key, Ref_or_Reason, ref_table);
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end
end
if
(somecondition()==true)->
delete_ref(Key, ref_table);
True->
do_nothing
end,    
{Packet, C2SState}.

现在一切正常,除了以下情况:

1.创建聊天室,A和A之间开始消息交换 B 和计时器也从此时开始。

  1. 如果创建聊天室的用户在时间 T 下线(通过最小化应用程序并从 android 设备杀死它(并重新上线,计时器停止,如计划在 30 秒结束时调用的函数不会被调用。此处突出显示"重新联机",因为如果用户未联机,计时器才会按预期工作,只有当用户再次联机时,计时器才会停止并且不会生成任何日志(。

    但是,如果在线用户现在在 T 发送任何消息,那么 随机选择消息并发送消息的整个周期性操作 对客户来说,重新开始得相当好,结束得很好。

    但是,如果在线用户此时未发送任何消息,则 T 然后安排的计时器永远不会被调用,用户保持打开 等待。

  2. 如果被邀请到聊天室的用户在时间 T2(例如 2 中的方式(脱机并再次联机,则计时器将保持活动状态并按预期工作。

因此,我将 ejabberd 的日志记录级别更改为 5,并看到发送到离线和再次在线用户的脱机消息没有传递。即使在 ejabberd.yml 中启用了mod_offline。

日志:

#message{
id = <<>>,type = error,lang = <<"en">>,
from = 
{jid,<<"fWiTvj973AB”>>,<<“example.com">>,<<"Smack">>,<<"fwitvj973ab”>>,
<<"example.com">>,<<"Smack">>},
to = 
{jid,<<"ac5a6b8c-66b8-4da7-8b1a-0f3ecb1e5gfd”>>,
<<"conference.example.com">>,<<"cXWmOrqEESd”>>,
<<"ac5a6b8c-66b8-4da7-8b1a-0f3ecb1e5gfd">>,
<<"conference.example.com">>,<<"cXWmOrqEESd">>},
subject = [#text{lang = <<>>,data = <<>>}],
body = [#text{lang = <<>>,data = <<""cXWmOrqEESd"">>}],
thread = undefined,
sub_els = 
[{xmlel,<<"q">>,[{<<"xmlns">>,<<"ns:custom”>>}],[]},
#stanza_error{
type = cancel,code = 503,by = <<>>,
reason = 'service-unavailable',
text = 
#text{lang = <<"en">>,data = <<"User session terminated">>},
sub_els = []}],
meta = #{}}

ejabberd.yml

###.  ============
###'  SHAPER RULES
shaper_rules:
## Maximum number of offline messages that users can have:
max_user_offline_messages:
- 5000: admin
- 100
###.  =======
###'  MODULES
##
## Modules enabled in all ejabberd virtual hosts.
##
modules:
mod_offline:
db_type: sql
access_max_user_messages: max_user_offline_messages
store_empty_body: unless_chat_state

虽然我不需要完美地传递这些离线消息,但我倾向于这样的想法,即这是否可以成为停止计时器的原因(但我不明白为什么它只在创建房间的用户离线并返回时停止,为什么不当其他用户这样做时?

为什么此计时器会停止,如何保持它定期运行?

这在timer模块的文档的最底部提到:

间隔计时器,

即通过计算任何函数apply_interval/4send_interval/3send_interval/2创建的计时器,链接到计时器执行其任务的进程。

因此timer:apply_interval将计时器服务器链接到启动计时器的进程,并且当调用进程退出时,计时器将被取消。

显然,计时器是从管理用户连接的进程创建的,因此当用户断开连接时,计时器会自动取消。

您可以通过生成管理此计时器的长时间运行的进程来解决此问题。


一个不相关的风格问题:在 Erlang 中,case通常比if更清晰。这段代码:

{Timer_Result,Ref_or_Reason} = timer:apply_interval(30000, ?MODULE, func(), [Arguments]),
if
(Timer_Result == ok)->
ets:insert(ref_table, {Key, Ref_or_Reason});
(Timer_Result == error)->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Ref_or_Reason])
end;

可以写成:

case timer:apply_interval(30000, ?MODULE, func(), [Arguments]) of
{ok, Ref} ->
ets:insert(ref_table, {Key, Ref});
{error, Reason} ->
io:format(" Could not delete user after timeout, Reason is ~p~n",[Reason])
end;

我建议阅读mod_muc_room.erl代码并使用muc的钩子。例如,当用户仅在聊天室(muc_filter_message挂钩(或用户在聊天室(muc_filter_presence挂机(中发送状态(加入、离开等(时,您可以收到通知。最好有一个处理计时器的过程(如mod_ping(。但是对于大规模,您必须使用ejabberd_c2s的c2s_handle_info和c2s_terminate钩来管理计时器。此外,我建议升级到Ejabberd 18.06或至少17.11。

相关内容

最新更新