Как создать глобальные переменные в 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