如何在 Erlang 中创建全局变量

Posted

技术标签:

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

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

我有 4 个重要的功能:

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

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

【问题讨论】:

【参考方案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)

【讨论】:

以上是关于如何在 Erlang 中创建全局变量的主要内容,如果未能解决你的问题,请参考以下文章

在 Visual Basic for Applications 中创建一个全局变量

在CakePHP中创建全局变量的最佳方法是什么?

在 SQL Server 中创建全局静态变量?

从 if 语句中创建一个全局变量

在 C# Web 浏览器中创建 javascript 变量全局范围

SwiftUI - 如何创建全局 @State 变量?