2009-06-11 30 views
1

未定義功能看到this article後,我一直在擺弄了Mochiweb。在嘗試複製文章中的內容時 - 基本上設置了一個mochiweb服務器,有兩個erlang節點,然後調用另一個節點中定義的函數(在兩個節點之間設置net_adm:ping()後,他們知道每個節點其他)。異常錯誤:到Mochiweb /二郎

我能夠關注一切,直到該函數調用部分。在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}; 

我只粘貼了代碼的相關部分。然而,當我現在訪問瀏覽器上的Web服務器 - 我得到的N1 @本地這個錯誤報告:

=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}} 

周圍的Googling後,我得到了什麼樣的錯誤是想說一個基本精神 - 基本上它說在n1 @ localhost中調用的登錄函數沒有定義 - 但是它在n2 @ localhost中定義(並且兩個節點彼此瞭解 - 我做了nodes().檢查)!請告訴我我哪裏出錯了!

回答

1

你是對的 - router:login的代碼實際上並不在你的主機上n1 @ localhost - 它是該函數中的代碼(gen_server:call function),它將呼叫路由到n2 @ localhost ?SERVER宏),這就是真正實現的地方。頂層函數只是將該調用包裝到適當節點的一種方式。

但是,你至少需要登錄

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

N1上@本地可用的實現。

(更新)

你需要確定,更換或我​​們?服務器宏爲好。在文章中的示例代碼這是

-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 @本地登錄會讓一個gen_server呼叫路由器的作用:handle_call方法對N2 @本地,假設gen_server進程正在運行的路由器和已經註冊了。該調用的返回值會返回到n1 @ localhost上的進程。

+0

這是否意味着我只需要在mochiweb/erl腳本(和聲明-export([login/2])中獲得上述兩行)。 當我這樣做,我得到「未定義的宏」'服務器''「錯誤。如果我使用「%-behaviour(gen_server)」,那麼我必須重寫默認功能。 – thomas55 2009-06-12 02:03:34

+0

或者我缺少其他明顯的東西?我是否需要將router.erl放置在與mochiweb腳本相同的文件夾中?當然,我不是那樣做的... – thomas55 2009-06-12 02:05:50

0

在你的問題的例子,它看起來像你只裝在一個節點上router模塊。節點不會默認自動從彼此加載代碼,所以僅僅因爲函數是在n2上定義的並不意味着你可以在n1上本地調用它(n1需要能夠以正常方式加載它)。

代碼給出看起來像它在分佈式系統中運行正常科佩斯(你可以在一個節點上啓動路由服務器,並呼籲其他節點的API函數將發送路由器請求傳送到正確的地方)。所以你只需要在n1上放置一個router模塊的副本,它應該可以工作。可以讓n1從n2加載router模塊,但與僅給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, [], []).