Ошибка исключения:неопределенная функция в Mochiweb/Erlang

StackOverflow https://stackoverflow.com/questions/982309

  •  13-09-2019
  •  | 
  •  

Вопрос

После просмотра Эта статья, я возился с mochiweb.Пытаясь повторить то, что сделано в статье - в основном настройка сервера mochiweb, наличие двух узлов erlang, а затем вызов функции, определенной в одном узле, в другом (после установки net_adm:ping() между двумя узлами, чтобы они знали каждый другой).

Я смог следить за всем, вплоть до этой части вызова функции.В n1@localhost, который является сервером mochiweb, я вызываю (так же, как сделано в статье):

router:login(IdInt, self()).

А затем в n2@localhost, который является сценарием router.erl, я определил функцию входа в систему:

login(Id, Pid) when is_pid(Pid) ->
    gen_server:call(?SERVER, {login, Id, Pid}).

handle_call({login, Id, Pid}, _From, State) when is_pid(Pid) ->
          ets:insert(State#state.pid2id, {Pid, Id}),
          ets:insert(State#state.id2pid, {Id, Pid}),
          link(Pid), % tell us if they exit, so we can log them out
          io:format("~w logged in as ~w\n",[Pid, Id]),
          {reply, ok, State};

Я вставил только соответствующие части кода.Однако, когда я теперь получаю доступ к веб-серверу через браузер, я получаю этот отчет об ошибке на n1@localhost:

=CRASH REPORT==== 11-Jun-2009::12:39:49 ===
  crasher:
    initial call: mochiweb_socket_server:acceptor_loop/1
    pid: <0.62.0>
    registered_name: []
    exception error: undefined function router:login/2
      in function  mochiconntest_web:loop/2
      in call from mochiweb_http:headers/5
    ancestors: [mochiconntest_web,mochiconntest_sup,<0.59.0>]
    messages: []
    links: [<0.61.0>,#Port<0.897>]
    dictionary: [{mochiweb_request_path,"/test/123"}]
    trap_exit: false
    status: running
    heap_size: 1597
    stack_size: 24
    reductions: 1551
  neighbours:

=ERROR REPORT==== 11-Jun-2009::12:39:49 ===
{mochiweb_socket_server,235,{child_error,undef}}

Погуглив, я получил основную суть того, что пытается сказать ошибка — в основном она говорит, что функция входа, вызываемая в n1@localhost, не определена — но она определена в n2@localhost (и оба узла знают каждый другое - я сделал nodes(). Проверять) !!Пожалуйста, скажите мне, где я ошибаюсь!

Это было полезно?

Решение

Вы правы - код для router:login на самом деле недоступен на вашем хосте n1@localhost - это код внутри этой функции (функция gen_server:call), которая направляет вызов на n2@localhost (через этот макрос ?SERVER) и вот где настоящая реализация.Функция верхнего уровня — это просто способ завершить вызов соответствующего узла.

Но вам нужна хотя бы реализация входа в систему

login(Id, Pid) when is_pid(Pid) ->
    gen_server:call(?SERVER, {login, Id, Pid}).

доступен на n1@localhost.

(Обновлено)

Вам также потребуется определить, заменить или использовать макрос ?SERVER.В примере кода в статье это

-define(SERVER, global:whereis_name(?MODULE)).

но здесь используется макрос ?MODULE, что в вашем случае было бы неправильно.По сути, когда процесс gen_server (маршрутизатор) запускается, он регистрируется как ?MODULE, в этом случае он сопоставляется с атомным «маршрутизатором», который могут видеть другие узлы (с использованием global:whereis_name(router) ).Итак, вы должны иметь возможность просто написать:

login(Id, Pid) when is_pid(Pid) ->
    gen_server:call(global:whereis_name(router), {login, Id, Pid}).

Таким образом, результатом вызова входа в систему на n1@localhost будет вызов gen_server метода router:handle_call на n2@localhost, при условии, что процесс gen_server маршрутизатора запущен и зарегистрировался.Возвращаемое значение этого вызова возвращается в ваш процесс на n1@localhost.

Другие советы

В примерах вашего вопроса похоже, что вы загрузили только router модуль на одном узле.Узлы по умолчанию не загружают код друг из друга автоматически, поэтому тот факт, что функция определена на n2, не означает, что вы можете вызывать ее локально на n1 (n1 должен иметь возможность загружать ее обычным способом).

Приведенный код выглядит так, как будто он правильно справляется с работой в распределенной системе (вы можете запустить сервер маршрутизатора на одном узле, а вызов функций API на других узлах будет отправлять запросы маршрутизатора в нужное место).Поэтому вам просто нужно поместить копию router модуль на n1, и он должен работать.Возможно, n1 загрузит router модуль из n2, но это немного хлопотно по сравнению с тем, чтобы просто предоставить n1 копию модуля в его пути к коду.

Интересно, в коде роутера это делать ненужно gen_server:call(global:whereis_name(?MODULE, Message)). как gen_server:call/2 функция сама знает, как искать глобальные регистрации. -define(SERVER, {global, ?MODULE}). будет работать нормально, если вы измените функцию start_link на start_link() -> gen_server:start_link(?SERVER, ?MODULE, [], []).

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top