Как создать глобальные переменные в Erlang
Я пишу модуль 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
.
Мой вопрос: как я могу взять Host
из start()
и поместить его в глобальную переменную, чтобы 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 может предоставить средства для того, что вы хотите, но я не могу с этим поделать.
Ответ 2
Это может показаться излишним, но вы можете подумать о внедрении очень простого 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}.
Ответ 3
Вы можете запустить новый процесс фильтрации сообщений и зарегистрировать его с помощью 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.
Ответ 4
Предположим, что вы фильтруете входящие пакеты, а затем # jid.lserver может быть вашим хостом.
Ответ 5
угадывание для вашего описания, чем в однодоменном развертывании ejabberd (без виртуальных хостов),
yo может получить локальный домен XMPP с помощью макроса "MYNAME" (см. ejabberd.hrl для определения).
Ответ 6
Вы определяете свою глобальную переменную на верхней части модуля... как ниже
-define (Your Variable, "your host name here").
например.
-define (RelayHost, "smtp.gmail.com").
и вы можете использовать эту глобальную переменную во всем своем методе в своем модуле.
io:fwrite("Global Value ~p", [?RelayHost]).
-AjAy