如何在Erlang中创建全局变量

时间:2010-01-06 15:26:13

标签: erlang global-variables hook ejabberd

我正在编写一个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
  5. 我的问题是,如何从Host获取start()并将其放入全局变量中,以便fetch_translation可以访问它?

8 个答案:

答案 0 :(得分:9)

“最简单的方法”是创建一个命名的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可能会提供你想要的设施,但我无法帮助你。

答案 1 :(得分:9)

听起来有点矫枉过正,但您可以考虑实施一个非常基本的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}.

答案 2 :(得分:1)

您可以启动新的邮件过滤流程并使用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 :(得分:1)

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

答案 4 :(得分:1)

猜测您的描述而不是单域ejabberd部署(无虚拟主机),

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

答案 5 :(得分:1)

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

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

例如

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

您可以在模块中的所有方法中使用此Global变量。

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

-AjAy

答案 6 :(得分:0)

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

答案 7 :(得分:0)

尝试使用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)