如果计算函数是自包含的,即不依赖于客户端C上的任何其他模块或函数,那么您需要做的就是使用
fun
(功能对象)。可以通过网络发送
fun
并由远程机器应用,在
fun
内部,发送方嵌入了他们的地址和获取答案的方法。因此,执行者可能只看到一个
fun
,他们可能会或可能不会给出参数,但在fun中,发送方已经强制使用一种方法,使答案自动发送回来。
fun
是许多任务的抽象,并且可以作为参数移动。在客户端,您可以编写以下代码:
%% 在客户端的某个地方
%% 客户端运行在 node() == 'client@domain.com' 上
-module(client).
-compile(export_all).
-define(SERVER,{server,'server@domain.com'}).
give_a_server_a_job(Number)-> ?SERVER ! {build_fun(),Number}.
build_fun()->
FunObject = fun(Param)->
Answer = Param * 20/1000, %% 这里进行计算
rpc:call('client@domain.com',client,answer_ready,[Answer])
end,
FunObject.
answer_ready(Answer)->
%%% 使用 Answer 做各种有趣的事情....
io:format("\n\t答案在这里:~p~n",[Answer]).
然后服务器有如下代码:
%%% 在服务器的某个地方
%%% 服务器运行在 node() == 'server@domain.com' 上
-module(server).
-compile(export_all).
start()-> register(server,spawn(?MODULE,loop,[])).
loop()->
receive
{Fun,Arg} ->
Fun(Arg), %% 服务器执行工作
%% 工作自动发送答案回到客户端
loop();
stop -> exit(normal);
_ -> loop()
end.
以这种方式,作业执行者不需要知道如何发送回复,
任务本身知道它将如何将答案发送回发送任务的人!我在几个项目中使用了这种通过网络发送功能对象的方法,非常酷!!!
#### 编辑 #####
如果您有一个递归问题,您可以使用
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>
我们看到的是一段模块代码,它被编译并从字符串动态加载。如果允许该库在服务器和客户端上使用,则每个实体都可以将代码作为字符串发送,然后在另一个实体上动态加载和执行。该代码可以在使用后卸载。让我们看一下斐波那契函数以及如何将其发送并在服务器上执行:
%% 这是我们要转换为字符串的常规斐波那契代码:
-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).
%% 转换为字符串格式后,代码如下:
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".
%% 然后客户端会将此字符串发送到服务器,服务器会动态加载并执行代码
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]).
%%% 在服务器上
loop(ServerState)->
receive
{string,StringCode,Fib,Arg} when Fib == fib ->
try dynamic_compile:load_from_string(StringCode) of
{module,AnyMod} ->
Answer = AnyMod:fib(Arg),
%%% 将答案异步发送回客户端
%%% 因为通道不同,不能让客户端等待
rpc:call('client@domain.com',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.
那段粗糙的代码可以展示我想要表达的内容。但是,请记得卸载所有无用的动态模块。同时,你可以有一种方法,在加载之前检查服务器是否已经加载了这样的模块。我建议你不要复制粘贴上面的代码。看看它并理解它,然后编写自己的版本来完成任务。成功!
fun(X) -> 2 + X end
和fun(X) -> X + some_module:func(X) end
。后者不是自包含的函数,因此无论将其发送到哪里,都必须在代码路径中包含模块some_module
,但前者的函数可以在任何地方执行,甚至在远程机器上也可以。 - Muzaaya Joshua