1

この記事を見た後、私はmochiwebをいじくり回しています。記事で行われていることを複製しようとしている間-基本的にmochiwebサーバーをセットアップし、2つのerlangノードを持ち、次に一方のノードで定義された関数をもう一方のノードで呼び出します(2つのノード間で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};

コードの関連部分のみを貼り付けました。ただし、ブラウザでWebサーバーにアクセスすると、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では利用できません-呼び出しをn2@ localhostにルーティングするのはその関数(gen_server:call関数)内のコードです(?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質問の例では、モジュールを1つのノードにのみロードしたように見えます。デフォルトでは、ノードは相互にコードを自動的にロードしません。したがって、関数がn2で定義されているからといって、n1でローカルに呼び出すことができるとは限りません(n1は通常の方法でコードをロードできる必要があります)。

与えられたコードは、分散システムでの実行に適切に対応しているように見えます(1つのノードでルーターサーバーを起動し、他のノードでAPI関数を呼び出すと、ルーター要求が正しい場所に送信されます)。したがって、routerモジュールのコピーをn1に配置するだけで、正常に機能するはずです。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, [], []).

于 2009-06-12T11:16:46.007 に答える