我的场景如下-我有一个客户端C函数foo()执行一些计算。
我想要一个服务器S,它不知道foo(),而是执行这个函数,并将结果发送回客户端。
我正在尝试确定在Erlang中执行此操作的最佳方法。我正在考虑:
- 代码热交换-即S中的"升级"代码,使其具有函数foo()。执行并发送回客户端。
- 在节点都适当注册的分布式方式下,按照S做一些事情!C: foo ()-用于将功能"发送"到进程/节点S
是否有我没有想到的其他方法(或语言的特性)?
谢谢你的帮助!
如果计算函数是自包含的,即不依赖于客户端C上的任何其他模块或函数,那么您需要做的是afun
(函数对象)。Afun
可以通过网络发送并由远程机器应用,并且在fun
中,发送者嵌入了他们的地址和获得答案的方法。因此执行者可能只看到afun
,他们可能会或可能不会给出一个参数,但在乐趣中,发送者已经强制了一个方法,通过该方法,答案将自动发送回来。fun
是一个事物中许多任务的抽象,它可以作为参数四处移动。
在客户端,您可以有这样的代码:
%%在客户端某处%%客户端运行在节点()上== 'client@domain.com'模块(客户端)。编译(export_all)。——(服务器,{服务器,"server@domain.com"})。give_a_server_a_job(数量)→服务器!} {build_fun(),号码。build_fun()→funnobject = fun(Param)->答案=参数* 20/1000,这里计算%%rpc:调用("client@domain.com"、客户、answer_ready[答案])最终,FunObject。answer_ready(回答)→%%%使用答案的各种有趣的事情....io:format("ntAnswer is here: ~p~n",[Answer])。
服务器有如下代码:
%%%在服务器的某个位置%%%服务器在节点()上运行== 'server@domain.com'模块(服务器)。编译(export_all)。start()→注册(服务器、产卵(?)模块,循环,[]))。循环()→收到{乐趣,Arg}→Fun(参数),%%服务器执行作业%% job自动返回答案%%到客户端循环();停止→退出(正常);_→循环()结束。
这样,作业执行器不需要知道如何发送回复,作业本身知道它将如何发送回复给发送作业的人!。我在几个项目中使用了这种通过网络发送功能对象的方法,它太酷了!!
#### EDIT #####
如果您有递归问题,您可以使用funs
操作递归。但是,在客户机和/或服务器上至少需要一个库函数来协助递归操作。创建一个函数,它应该在客户端和服务器的代码路径中。
另一种选择是动态地将代码从服务器发送到客户端,然后使用库:Dynamic Compile erlang
从客户端在服务器上加载和执行erlang代码。下面是一个使用动态编译的例子:
1>字符串= "-模块(添加)。 n出口([添加/2])。n add(A,B) ->A + b n"。"模块(添加)。 n出口([添加/2])。n add(A,B) ->A + b n"2比;dynamic_compile: load_from_string(字符串)。{模块,添加}3比;添加:添加(2、5)。74在上面我们看到的是一段从字符串动态编译和加载的模块代码。如果启用此功能的库在服务器和客户机上可用,则每个实体都可以将代码作为字符串发送,并在另一个实体上动态加载和执行。此代码可以在使用后卸载。让我们来看看斐波那契函数,以及它是如何在服务器端发送和执行的:
%%这是我们要转换成字符串的正常斐波那契代码:-module(fib)。出口([fib/1])。当N == 0 ->时fib(N);0;fib(N) when (N <3)和(N>0)→1;fib(N)当N>0→fib(N-1) + fib(N-2)%%在字符串格式中,这将变成这段代码StringCode = " -module(fib)。 n出口([fib/1])。nfib(N)当N == 0 ->n fib(n) when (n <3)和(N>0)→1;n fib(n)当n>0→fib(N-1) + fib(N-2) n"。%%然后客户端将上述字符串发送到服务器,服务器将
%%动态加载代码并执行它
send_fib_code(Arg)-> {ServerRegName,ServerNode} !{字符串、StringCode fib, Arg},好的。get_answer({fib,of,This,is,That}) -> io:format("Fibonacci (from server) of ~p is: ~p~n",[This,That])。%%% At Serverloop(ServerState)->receive{string,StringCode,Fib,Arg}当Fib == Fib ->尝试dynamic_compile:load_from_string(StringCode) of{模块,AnyMod} ->答案= AnyMod:fib(Arg),%%%将应答发送回客户端%%%应该是异步的%%%为不同的频道&不制作%%客户端等待rpc:调用("client@domain.com"、客户、get_answer [{fib,参数,,回答}))抓_:_ -> error_logger:error_report(["从客户端动态编译和加载模块失败"])最终,循环(ServerState);_ -> loop(ServerState)结束。
这段粗略的代码可以告诉你我想说什么。但是,要记得卸载所有不可用的动态模块。此外,您还可以使用一种方法,让服务器在再次加载模块之前尝试检查该模块是否已经加载。我建议你不要复制粘贴上面的代码。看看它,理解它,然后写出你自己的版本,可以完成这项工作。
成功!!
如果您执行S ! C:foo()
,它将从模块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/2
和rpc:call/4,5
是如何实现的,这就是为什么有OTP来拯救你的原因。