首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Timer:apply_interval/4在用户进入oflline并在ejabberd中再次返回时停止

Timer:apply_interval/4在用户进入oflline并在ejabberd中再次返回时停止
EN

Stack Overflow用户
提问于 2018-08-21 06:51:50
回答 2查看 134关注 0票数 0

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

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

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

我这样做的原因如下:

代码语言:javascript
复制
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和B之间开始消息交换,此时计时器也开始。

  1. 如果创建聊天室的用户在时间上离线,比如T(通过最小化应用程序并在安卓设备上杀死它)并恢复在线,则计时器停止(就像计划在30秒结束时调用的函数中的那样)不会被调用。(这里突出显示,如果用户不在线,计时器就会正常工作,只有当用户再次在线时,计时器就停止了,没有生成日志)。 但是,如果在线用户现在在T点发送任何消息,那么随机选择消息并发送到客户端的整个周期操作将再次启动,并结束。 但是,如果在线用户在这个时间点没有发送任何消息,那么调度的计时器就不会被调用,用户就会一直等待。
  2. 如果被邀请到聊天室的用户在时间上离线,T2(如2所示)再次上线,那么定时器将保持活动状态,并按预期工作。

因此,我将ejabberd的日志记录级别更改为5,并且再次看到了给离线和联机用户的脱机消息没有得到delivered.even,尽管在ejabberd.yml中启用了mod_offline。

日志:

代码语言:javascript
复制
#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

代码语言:javascript
复制
###.  ============
###'  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

虽然我不需要这些离线消息能够完美地传递,但我倾向于认为它是否是停止我的计时器的原因(但我不明白为什么只有当创建房间的用户离线并返回时,它才会停止,为什么其他用户会这样做?)

为什么这个计时器会被停止,我如何使它定期运行?

EN

回答 2

Stack Overflow用户

发布于 2018-08-22 13:45:54

模块的最底层提到了这一点

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

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

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

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

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

代码语言:javascript
复制
 {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;

可以写成:

代码语言:javascript
复制
 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;
票数 1
EN

Stack Overflow用户

发布于 2018-08-22 23:46:38

我建议阅读mod_muc_room.erl代码并使用muc的钩子。例如,当用户仅在聊天室(muc_filter_message钩子)中发送消息时,或者当用户在聊天室(muc_filter_presence钩子)中发送存在(连接、离开等)时,可以通知您。最好有一个处理定时器的进程(如mod_ping)。但是在大规模的情况下,您必须使用ejabberdC2s的c2s_handle_info和c2s_terminate挂钩来管理计时器。此外,我建议升级到Ejabberd 18.06或至少17.11因为

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51943132

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档