Erlang:将客户端进程/功能卸载到服务器?

时间:2011-10-27 14:07:29

标签: erlang distributed-computing hotswap

我的情况如下 - 我有一个带有函数foo()的客户端C,它执行一些计算。

我想要一个不知道foo()的服务器S来代替执行这个函数,并将结果发送回客户端。

我正在尝试确定在Erlang中执行此操作的最佳方法。我在考虑:

  • 热代码交换 - 即S中的“升级”代码,使其具有函数foo()。执行并发送回客户端。
  • 以节点均已正确注册的分布式方式,按照 S的方式执行操作! C:foo() - 用于将函数“发送”到进程/节点S

我还没有考虑其他方法(或语言的特征)吗?

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

如果计算函数是自包含的,即不依赖于客户端C上的任何其他模块或函数,那么您需要做的是 fun (功能对象)。可以通过网络发送 fun 并通过远程计算机应用 fun ,发件人已嵌入其地址和得到答案的方式。所以执行者可能只看到 fun 他们可能会或可能不会给出一个参数,但在有趣的情况下,发送者强制使用一种方法,其中答案将自动返回。 fun 是一件事中很多任务的抽象,它可以作为参数移动。
在客户端,您可以使用以下代码:


%% somewhere in the client
%% client runs on 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, %% computation here
                    rpc:call('client@domain.com',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() == 'server@domain.com'

-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操作递归。但是,您将需要在客户端和/或服务器上至少有一个库函数来协助递归操作。创建一个应该在客户端和服务器的代码路径中的函数。

另一种选择是动态地将代码从服务器发送到客户端,然后使用库: 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>

我们在上面看到的是一段模块代码,它是从字符串动态编译和加载的。如果启用此功能的库在服务器和客户端可用,则每个实体都可以将代码作为字符串发送,并在另一个实体上动态加载和执行。使用后可以卸载此代码。让我们看一下Fibonacci函数以及如何在服务器上发送和执行它:

%% 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('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.

这段粗略的代码可以告诉你我想说什么。但是,您记得卸载所有不可用的动态模块。此外,你可以有一种方法,服务器尝试检查已经加载这样的模块已经加载再次加载之前。我建议你不要复制并粘贴上面的代码。看看并理解它,然后编写自己的版本,可以完成这项工作。
成功!!!

答案 1 :(得分:2)

如果您执行S ! C:foo(),它将从模块foo/1计算客户端函数C,并将其结果发送到进程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是如何实现的,这也是为什么有OTP可以帮助您避免大部分问题的原因。

相关问题