如何在 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 中创建一个全局变量