de erro de excepção: Função indefinida na Mochiweb / Erlang
Pergunta
Depois de ver este artigo , eu fui mexer com mochiweb. Enquanto tenta replicar o que está feito no artigo - basicamente como configurar um servidor mochiweb, tendo dois nós Erlang, e em seguida, chamar uma função definida em um nó na outra (depois de definir net_adm: mesa de ping () entre os dois nós para que eles conheçam cada outro).
Eu era capaz de acompanhar tudo até essa parte chamada de função. Em n1 @ localhost, que é o servidor mochiweb, eu chamo (assim como fez no artigo):
router:login(IdInt, self()).
E, em seguida, no n2 @ localhost, que é o script router.erl, tenho definido a função de login:
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};
Eu ter colado apenas as partes relevantes do código. No entanto, quando eu agora acessar o servidor web no navegador - eu recebo este relatório de erro no 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}}
Depois de googling ao redor, eu tenho uma essência básica do que o erro está tentando dizer - basicamente diz que a função de login sendo chamado no n1 @ localhost não está definido - mas ele está definido no n2 @ localhost (e tanto o nós conhecemos - Eu fiz nodes().
para verificar) !! Por favor, diga-me onde estou indo errado!
Solução
Você tem razão - o código para router: login não está realmente disponível em seu host n1 @ localhost - é o código dentro dessa função (a gen_server: função de chamada) que encaminha a chamada para n2 @ localhost (via isso? macro SERVER) e é aí que a implementação real é. A função de nível superior é simplesmente uma maneira de envolver-se que a chamada para o nó apropriado.
Mas você precisa, pelo menos, a implementação de logon
login(Id, Pid) when is_pid(Pid) ->
gen_server:call(?SERVER, {login, Id, Pid}).
disponível em n1 @ localhost.
(Actualizado)
Você precisa de definir, substituir ou nos uma? SERVIDOR macro também. No código de exemplo no artigo este é
-define(SERVER, global:whereis_name(?MODULE)).
mas isso usa o? MÓDULO macro que seria errado no seu caso. ? Basicamente, quando o processo gen_server (router) é iniciado ele se registra como MÓDULO, neste caso, que mapeia para o átomo 'router' que outros nós pode ver (usando mundial: whereis_name (router)). Então você deve ser capaz de escrever apenas:
login(Id, Pid) when is_pid(Pid) ->
gen_server:call(global:whereis_name(router), {login, Id, Pid}).
Assim, o efeito de logon convidando n1 @ localhost iria fazer uma chamada gen_server ao roteador: método handle_call no n2 @ localhost, assumindo o processo gen_server roteador está em execução e foi registrada. O valor de retorno da chamada volta para o seu processo on n1 @ localhost.
Outras dicas
Nos exemplos em sua pergunta, parece que você só carregado o módulo router
em um nó. Nós não por padrão carregar automaticamente o código de eachother, por isso só porque a função é definida no n2 não significa que você pode chamá-lo localmente no n1 (n1 precisaria ser capaz de carregá-lo na maneira normal).
O código dado parece que lida adequadamente com o funcionamento em um sistema distribuído (você pode iniciar o servidor roteador em um nó e chamando as funções de API em outros nós enviará solicitações roteador para o local correto). Então você só precisa colocar uma cópia do módulo router
em n1 e ele deverá funcionar. É possível ter n1 carregar o módulo router
de n2, mas é um pouco de um aborrecimento em comparação com apenas dando n1 uma cópia do módulo em seu caminho de código.
Curiosamente, no código roteador é desnecessário fazer gen_server:call(global:whereis_name(?MODULE, Message)).
como a função gen_server:call/2
sabe para procurar-se inscrições globais. -define(SERVER, {global, ?MODULE}).
iria funcionar bem se você mudou a função start_link para start_link() -> gen_server:start_link(?SERVER, ?MODULE, [], []).