Как создать глобальные переменные в Erlang - PullRequest
15 голосов
/ 06 января 2010

Я пишу модуль 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.

У меня вопрос, как я могу взять Host из start() и поместить его в глобальную переменную, чтобы fetch_translation мог получить к ней доступ?

Ответы [ 6 ]

9 голосов
/ 19 мая 2011

Это может звучать как излишнее количество, но вы можете подумать о реализации очень простого 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}.
9 голосов
/ 06 января 2010

Самый простой способ - создать именованную таблицу 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 голос
/ 09 февраля 2012

Вы определяете свою глобальную переменную в верхней части вашего модуля ... как показано ниже

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

например.

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

и вы можете использовать эту глобальную переменную во всех ваших методах в вашем модуле.

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

-AjAy

1 голос
/ 01 февраля 2010

угадав для вашего описания, чем вы находитесь в однодоменном развертывании ejabberd (без виртуальных хостов),

вы можете получить локальный домен XMPP с помощью макроса? MYNAME (см. Определение ejabberd.hrl).

1 голос
/ 17 января 2010

Скажем, вы фильтруете входящие пакеты, тогда To # jid.lserver может быть вашим хостом.

1 голос
/ 13 января 2010

Вы можете запустить новый процесс фильтрации сообщений и зарегистрировать его, используя 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.
...