如果計算功能是自包含的,即不依賴於客戶端C上的任何其他模塊或功能,那麼您需要做的是一個fun
(功能對象)。 A fun
可以通過網絡發送並通過遠程機器應用,並且在發送方已經嵌入他們的地址和獲得答案的方式。所以執行者只能看到一個fun
他們可能會或可能不會提供一個參數,但在樂趣裏面,發件人已經強制一個方法,通過答案自動發回。 fun
是一件事物中很多任務的抽象,它可以作爲參數移動。
在客戶端,你可以有這樣的代碼:
%% 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.
這樣,作業執行需要不知道如何發送回答復,工作本身來了解它將如何發回答案,然而發送工作!。我已經使用這種方法在幾個項目中通過網絡發送功能對象,它非常酷!
#### EDIT #####
如果你有一個遞歸問題,您可以使用funs
處理遞歸。但是,客戶端和/或服務器至少需要一個庫函數來協助遞歸操作。創建一個函數,該函數應該位於客戶端以及服務器的代碼路徑中。
另一種方法是動態地將代碼從服務器發送到客戶端,然後使用庫: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.
那塊粗糙的代碼可以告訴你什麼想說的。但是,您記得卸載所有不可用的動態模塊。你也可以有一種方法,在服務器嘗試檢查這個模塊是否在加載之前已經加載。我建議你不要複製和粘貼上面的代碼。看看它並理解它,然後編寫可以完成這個工作的自己的版本。
成功!!!
@ Muzayya-Joshua謝謝,這個答案是非常有幫助的。注意:客戶端代碼中的rpc:call後面有一個額外的逗號。如果我想發送一個遞歸函數到服務器,以斐波那契爲例?或者,如果我沒有自包含的功能,而且我想將整個調用堆棧移到服務器呢?感謝您的幫助。 – Eitan
如果你想要一個遞歸的fun(),你可能會喜歡一個Erlang y-combinator:http://bc.tech.coop/blog/070611.html – MatthewToday
** @ Muzayya-Joshua **引用你的原始代碼/回答:如果客戶端和服務器從同一個目錄運行,傳遞Fun將正常工作。但是,如果客戶機和服務器不在同一個目錄下(真實情況),我在節點'server @ ...'上得到_Error進程<0.39.0>,退出值爲:{undef,[{#Fun,[5 ]},{server,loop,0}]} _ **我的問題:服務器似乎在尋找客戶端來執行該功能。爲什麼會發生這種情況?** –
Eitan