2011-10-27 8 views
4

私のシナリオは次の通りです。 - 私は、計算を実行する関数foo()を持つクライアントCを持っています。Erlang:クライアントプロセス/機能をサーバーにオフロードしますか?

代わりにこの関数を実行し、結果をクライアントに送り返すためにfoo()について知らないサーバーSを希望します。

私はErlangでこれを実行する最良の方法を決定しようとしています。私は考慮しています:

  • ホットコードスワッピング - つまり、関数foo()を持つようにSのコードを "アップグレード"します。実行してクライアントに送り返します。
  • ノードがすべて適切に登録されている分散方法では、S! C:foo() - 処理/ノードに関数を送るために

私が考えていない他の方法(または言語の機能)はありますか?

ありがとうございました!あなたがS ! C:foo()を行う場合

答えて

3

計算機能が自己完結型である場合、つまりクライアントCの他のモジュールや関数に依存しない場合は、fun(機能オブジェクト)が必要です。 A funは、ネットワークを介して送信し、リモートマシンによって適用することができ、その側にはfunと入力します。送信者は、アドレスと返信方法を埋め込みます。したがって、実行者は、引数を与えても与えなくても、funしか見ることができませんが、送信者は答えが自動的に返される方法を強制しています。 funは、1つの事の中で非常に多くのタスクを抽象化しており、引数として移動することができます。クライアントで
、あなたがこのようなコード持つことができます。

 

%% somewhere in the client 
%% client runs on node() == '[email protected]' 

-module(client). 
-compile(export_all). 
-define(SERVER,{server,'[email protected]'}). 

give_a_server_a_job(Number)-> ?SERVER ! {build_fun(),Number}. 

build_fun()-> 
    FunObject = fun(Param)-> 
        Answer = Param * 20/1000, %% computation here 
        rpc:call('[email protected]',client,answer_ready,[Answer]) 
       end, 
    FunObject. 

answer_ready(Answer)-> 
    %%% use Answer for all sorts of funny things.... 
    io:format("\n\tAnswer is here: ~p~n",[Answer]). 

をサーバは、このようなコードがあります。このように

 

%%% somewhere on the server 
%%% server runs on node() == '[email protected]' 

-module(server). 
-compile(export_all). 

start()-> register(server,spawn(?MODULE,loop,[])). 

loop()-> 
    receive 
     {Fun,Arg} -> 
      Fun(Arg), %% server executes job 
         %% job automatically sends answer back 
         %% to client 
      loop(); 
     stop -> exit(normal); 
     _ -> loop() 
    end. 

を、ジョブエグゼキュータは戻って送信する方法を知っている必要はありません返信、仕事自体は、仕事を送ったが、答えを返す方法を知って来る!。私はいくつかのプロジェクトでネットワークを介して機能オブジェクトを送信するこの方法を使用しました。

####あなたは再帰的な問題を持っている場合は、あなたがfunsを使用して再帰を操作

##### EDIT。ただし、再帰的操作を支援するには、クライアントおよび/またはサーバーで少なくとも1つのライブラリ関数が必要です。クライアントとサーバーのコードパスにある関数を作成します。

もう1つの方法は、サーバからクライアントへコードを動的に送信し、次にライブラリを使用することです。Dynamic Compile erlangクライアントからサーバーにerlangコードをロードして実行します。動的コンパイルを使用すると、次のようになります。

 
1> String = "-module(add).\n -export([add/2]). \n add(A,B) -> A + B. \n". 
"-module(add).\n -export([add/2]). \n add(A,B) -> A + B. \n" 
2> dynamic_compile:load_from_string(String). 
{module,add} 
3> add:add(2,5). 
7 
4> 

上記の内容は、文字列からコンパイルされ、動的にロードされるモジュールコードです。これを可能にするライブラリがサーバーとクライアントで利用可能な場合、各エンティティはコードを文字列として送信し、コードを別のエンティティで動的にロードして実行できます。このコードは、使用後にアンロードすることができます。

 
%% This is the normal Fibonacci code which we are to convert into a string: 

-module(fib). 
-export([fib/1]). 

fib(N) when N == 0 -> 0; 
fib(N) when (N < 3) and (N > 0) -> 1; 
fib(N) when N > 0 -> fib(N-1) + fib(N-2). 

%% In String format, this would now become this piece of code 
StringCode = " -module(fib).\n -export([fib/1]). \nfib(N) when N == 0 -> 0;\n fib(N) when (N < 3) and (N > 0) -> 1;\n fib(N) when N > 0 -> fib(N-1) + fib(N-2). \n". 

%% Then the client would send this string above to the server and the server would 
%% dynamically load the code and execute it

send_fib_code(Arg)-> {ServerRegName,ServerNode} ! {string,StringCode,fib,Arg}, ok. get_answer({fib,of,This,is,That}) -> io:format("Fibonacci (from server) of ~p is: ~p~n",[This,That]). %%% At Server loop(ServerState)-> receive {string,StringCode,Fib,Arg} when Fib == fib -> try dynamic_compile:load_from_string(StringCode) of {module,AnyMod} -> Answer = AnyMod:fib(Arg), %%% send answer back to client %%% should be asynchronously %%% as the channels are different & not make %% client wait rpc:call('[email protected]',client,get_answer,[{fib,of,Arg,is,Answer}]) catch _:_ -> error_logger:error_report(["Failed to Dynamic Compile & Load Module from client"]) end, loop(ServerState); _ -> loop(ServerState) end.

ラフコードの一部が言うしようとしているものをお見せできること:フィボナッチ機能を見て、どのようにサーバーに送信され、実行することができます。ただし、使用できないすべての動的モジュールをアンロードすることを忘れないでください。また、サーバーは、そのようなモジュールがすでにロードされているかどうかをチェックしてから、再度ロードすることもできます。上記のコードをコピー&ペーストしないことをお勧めします。それを見てそれを理解してから、自分の仕事をすることができるバージョンを書いてください。
成功!

+1

@ Muzayya-Joshuaありがとう、この回答は非常に役に立ちました。注:クライアントコードでrpc:callの後に余分なカンマがあります。サーバーに再帰関数を送信する場合は、たとえばフィボナッチを使用しますか?あるいは、自己完結型の関数を持たず、呼び出しスタック全体をサーバーに移動したいのですが?すべての助けをありがとう。 – Eitan

+2

再帰的なfun()をしたい場合は、Erlang y-combinatorが好きかもしれません:http://bc.tech.coop/blog/070611.html – MatthewToday

+0

** @ Muzayya-Joshua **オリジナルのコードを参照してください/ answer:クライアントとサーバーが同じディレクトリから実行されている場合、Funを渡すとうまく動作します。しかし、クライアントとサーバが同じディレクトリに存在しない場合(現実的なシナリオ)、出口値を持つノード 'server @ ...'上のプロセス<0.39.0>の_Errorを取得します:{undef、[{Fun 、[5 ]}、{サーバー、ループ、0}]} _ **私の質問:サーバーは、クライアントがその機能を実行するのを探しているようです。 ** – Eitan

2

それはモジュールCからクライアント側機能foo/1に計算し、Sを処理するために、その結​​果を送信します。あなたがしたいことのようには見えません。

% In client 

call(S, M, F, A) -> 
    S ! {do, {M, F, A}, self()}, 
    receive 
    {ok, V} -> V 
    end. 

% In server 

loop() -> 
    receive 
    {do, {M, F, A}, C} -> 
     C ! {ok, apply(M, F, A)}, 
     loop() 
    end. 

しかし、実際のシナリオでは、もっと多くの作業が必要です。 (make_ref/0)を実行するクライアントメッセージをマークし、サーバーでエラーをキャッチしてクライアントに送り返し、クライアントからサーバーを監視してサーバーを停止させ、タイムアウトを追加します。どのようにgen_server:call/2rpc:call/4,5が実装されているのか見てみましょう。ほとんどのgotchaからあなたを救うためのOTPがあるのはなぜですか。

関連する問題