【问题标题】:How to create global variables in Erlang如何在 Erlang 中创建全局变量
【发布时间】:2011-01-02 02:33:44
【问题描述】:

我正在编写一个 ejabberd 模块来过滤数据包。我需要获取主机名以使用gen_mod:get_module_opt() 提取一些配置。

我有 4 个重要的功能:

  1. start(Host, _Opt) :这是一个加载我的模块的 ejabberd 函数。我在这里得到Host 原子
  2. filter_packet({From, To, XML}):这是我的包过滤钩子。我不能将自定义参数传递给这个函数,因为它是 ejabberd 中的一个钩子。
  3. get_translation(XmlData): filter_packet() 循环调用get_translation()
  4. fetch_translation(XmlData):从 get_translation() 递归调用。这是我打电话给gen_mod:get_module_opt()的地方,因此需要Host

我的问题是,如何从start() 中取出Host 并将其放入全局变量中,以便fetch_translation 可以访问它?

【问题讨论】:

    标签: erlang global-variables hook ejabberd


    【解决方案1】:

    “最简单的方法”是创建一个命名的 ets 表,并将其放入其中。

    start(Host, _Opt) ->
      ets:new(my_table, [named_table, protected, set, {keypos, 1}]),
      ets:insert(my_table, {host, Host}),
      ...
    
    fetch_translation(XmlData) ->
      [{_, Host}] = ets:lookup(my_table, host),
      ...
    

    请注意,这是一个“通用”解决方案。 Ejabberd 可能会为您提供所需的设施,但我无法帮助您。

    【讨论】:

    • 谢谢。该模块编译正常,但为 'ets:new(my_table, [named_table, protected, set, {keypos, 1}]),' 给出了一个 'badarg' 错误
    • 如果表 'my_table' 存在,你会得到一个 badarg。您要么需要检查表是否已经存在,要么将其包装在 try-catch 块中
    • 我不会在任何地方创建“my_table”,除了上面给出的。
    • 我迟到了,但我不会这样做,它会产生不必要的开销。最简单的解决方案是使用 To#jid.lserver,或者如果您需要更多的东西,您可以将其实现为 gen_server。
    • 这是一个比使用 OTP 进程更好的解决方案,OTP 进程会序列化每个调用,而且速度更慢。
    【解决方案2】:

    您可以启动一个新的消息过滤进程并使用erlang:register/2 注册它,然后通过它路由所有filter_packet/1 请求(潜在的瓶颈)。

    -define(?SERVER, msg_filter).
    
    start(Host, Opt) ->
       {ok, Pid} = spawn(?MODULE, filter_loop, [Host, Opt]),
       register(?SERVER, Pid).
    
    filter_loop(Host, Opt) ->
       receive
          {Pid, filter_packet, {_From, _To, XML}} ->
               Trans = get_translation(XML, Host),
               Pid ! {?SERVER, translation, Trans}, 
               filter_loop(Host, Opt)
       end.
    
    filter_packet(Pack) ->
       ?SERVER ! {self(), filter_packet, Pack}
       receive 
          {?SERVER, translation, Trans} ->
               % wrap translation
               UpdatedPacket
       end.
    

    【讨论】:

      【解决方案3】:

      假设您正在过滤传入的数据包,那么 To#jid.lserver 可能是您的主机。

      【讨论】:

      • 嗨,andi5。你能解释一下“To#jid.lserver”是什么意思吗?我该如何设置/获取它?
      • 这只是最好的猜测,但我认为 To 是绑定到 jid 类型记录的变量(请参阅底部 src/jlib.hrl 中的记录定义)。 To#jid.lserver 表示要访问记录的 lserver 字段,其中 lserver 是 jid 的域的小写版本。如果您在 shell 中遇到问题,请运行 rd(jid, {user,[...]})。
      【解决方案4】:

      猜测你的描述比你在单域 ejabberd 部署中(没有虚拟主机),

      你可以使用 ?MYNAME 宏获取本地 XMPP 域(有关定义,请参见 ejabberd.hrl)。

      【讨论】:

        【解决方案5】:

        这听起来有点矫枉过正,但您可以考虑实现一个非常基本的 gen_server。它包含一个对其回调可用的状态,并且数据可以保存在那里。对于您的情况,您可以编写一个类似于此的模块:

        -module(your_module_name).
        
        -behaviour(gen_server).
        -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
        
        -export([start/2, filter_loop/1]).
        
        start(Host, Opt) ->
          %% start the named gen server
          gen_server:start({local, ?MODULE}, ?MODULE, Host, []).
        
        filter_packet({From, To, XML}) ->
          %% do your thing
          gen_server:call(?MODULE, {fetch_translation, XmlData}).
        
        %% this will be called by gen_server:start - just pass the Host
        init(Host) ->
          {ok, Host}.
        
        handle_call({fetch_translation, XmlData}, _From, Host) ->
          %% do your thing
          {reply, ok, Host}.
        
        %% you can ignore the rest - they are needed to be present
        handle_cast(_Msg, State) ->
          {noreply, State}.
        handle_info(_Info, State) ->
          {noreply, State}.
        code_change(_OldVsn, State, _Extra) ->
          {ok, State}.
        

        【讨论】:

          【解决方案6】:

          您在模块顶部定义全局变量...如下所示

          -define (Your Variable, "your host name here").
          

          例如。

          -define (RelayHost, "smtp.gmail.com").
          

          并且您可以在模块中的所有方法中使用此全局变量。

          io:fwrite("Global Value ~p", [?RelayHost]).
          

          -AjAy

          【讨论】:

          • -define 不定义变量。它定义了编译时常量。
          • 无法定义全局变量。
          【解决方案7】:

          您不能创建全局变量,但您可以在函数之外定义一条记录,并使用属性创建该记录的实例,然后将其传递给您调用的方法。因此,您只能通过方法参数共享一条记录。

          【讨论】:

            【解决方案8】:

            尝试使用persistent_term:

            1> persistent_term:put(hello, <<"world">>).
            ok
            2> persistent_term:get(hello).       
            <<"world">>
            3> persistent_term:erase(hello).
            true
            4> persistent_term:get(hello).  
            ** exception error: bad argument
                 in function  persistent_term:get/1
                    called as persistent_term:get(hello)
            
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-11-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多