1

看到这篇文章后,我一直在修修补补mochiweb。在尝试复制文章中所做的事情时 - 基本上是设置一个 mochiweb 服务器,有两个 erlang 节点,然后在另一个节点中调用一个定义在一个节点中的函数(在两个节点之间设置 net_adm:ping() 以便他们知道每个节点之后)其他)。

在函数调用部分之前,我能够跟踪所有内容。在作为 mochiweb 服务器的 n1@localhost 中,我调用(就像在文章中所做的那样):

router:login(IdInt, self()).

然后,在 router.erl 脚本 n2@localhost 中,我定义了登录函数:

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().检查过)!请告诉我哪里出错了!

4

2 回答 2

1

你是对的 - 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 上调用 login 的效果将使 gen_server 调用 n2@localhost 上的 router:handle_call 方法,假设路由器 gen_server 进程正在运行并已注册自己。该调用的返回值返回到您在 n1@localhost 上的进程。

于 2009-06-11T19:33:04.807 回答
0

在您问题的示例中,您似乎只router在一个节点上加载了模块。默认情况下,节点不会自动从彼此加载代码,因此仅仅因为函数是在 n2 上定义的,并不意味着您可以在 n1 上本地调用它(n1 需要能够以正常方式加载它)。

给出的代码看起来可以很好地应对在分布式系统中的运行(您可以在一个节点上启动路由器服务器,并在其他节点上调用 API 函数将路由器请求发送到正确的位置)。因此,您只需将router模块的副本放在 n1 上,它就可以工作了。可以让 n1router从 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, [], []).

于 2009-06-12T11:16:46.007 回答