对动态生成的gen_server执行同步操作

时间:2014-06-04 20:05:39

标签: erlang otp gen-server

我很难绕着正确的方式绕过一个gen_server实例进行调用,该实例是由具有simple_one_for_one子策略的主管动态创建的。我试图创建数据访问控制作为gen_servers。每个实体都有自己的主管,该主管将根据需要创建gen_server实例,以实际对数据库执行CRUD操作。我理解定义子进程的过程,以及根据需要创建它们的过程。

最初,我的计划是将子创建过程抽象为创建子项的gen_server模块中的自定义函数,使用gen_server:call()启动对该子项的请求操作(例如查找,存储,删除),以及然后将操作结果返回给调用进程。但是,除非我弄错了,否则会阻止尝试使用这些函数的任何其他进程,直到调用返回。这绝对不是我的想法。

我可能会陷入OO模式(我的背景是Java),但似乎应该有一种干净的方法允许一个模块中的函数获取对子进程的引用,然后在没有该进程的情况下进行调用泄露那个孩子的内部。换句话说,我不想在实体主管上调用create_child()方法,然后让我的应用程序代码对该子PID进行gen_server:调用(即gen_sever:call(Pid, {find_by_id, Id}))。我希望能够调用更像Child:find_by_id(Id)的函数。

3 个答案:

答案 0 :(得分:1)

完整答案高度依赖于您的应用程序 - 例如,一个gen_server可能就足够了,或者您可能真的需要一个数据库连接池。但是您应该注意的一件事是gen_server可以从handle_call回调中返回,然后通过返回{noreply, NewState}实际为客户端准备好回复,然后一旦有gen_server:reply/2客户端回复就绪,调用gen_server将其发送回客户端。这允许gen_server为来自其他客户端的呼叫提供服务,而不会在第一次呼叫时阻塞。请注意,这要求gen_server:handle_info/2有一种方法可以将请求发送到数据库,而不必阻止等待回复;这通常是通过让数据库发送一个到达gen_server回调的回复,传递足够的信息,gen_server:call/2,3可以将数据库回复与正确的客户端请求相关联来实现的。另请注意,{{1}}的默认超时时间为5秒,因此如果您希望数据库调用的持续时间超过默认值,则需要处理此问题。

答案 1 :(得分:0)

如果您看到的问题是您泄漏了db-process的内部实现是gen_server,那么您可以实现api,以便它也将pid作为参数。

-module(user).

-behaviour(gen_server).

-export([find_by_id/2]).

find_by_id(Pid, Id) ->
    gen_server:call(Pid, {find_by_id, Id}).

%% Lots of code omitted

handle_call({find_by_id, Id}, From, State) ->
    ok.

%% Lots more code omitted.

这样你就不会告诉客户这个实现实际上是gen_server(尽管有人可以使用gen_server:call)。

答案 2 :(得分:0)

创建,修改或删除记录时,您无需等待答案。您可以为此使用gen_server:cast,但是您不需要gen_server,正如我在第一条评论中所说,在客户端进程中执行的对接口函数的简单调用将节省时间。

如果你想阅读,2例:

  • 你可以在等待答案时做点什么,然后gen_server调用就可以,但是一个简单的衍生过程等待答案并将其发送回客户端将提供相同的服务。

    < / LI>
  • 在得到答案之前你不能做任何事情,然后没有阻塞问题,我认为最好使用尽可能少的代码,所以再一次简单的函数调用就足够了。

gen_server意味着持久化并对消息做出反应。我没有在你的例子中看到需要坚持不懈。

-module(access).
-export([add/2,get/1]).
-record(foo, {bar, baz}).

add(A,B) ->
  F = fun() ->
    mnesia:write(#foo{bar=A,baz=B})
  end,
  spawn(mnesia,activity,[transaction, F]). %% the function return immediately,
                                           %% but you will not know if the transaction failed

get(Bar) ->
  F = fun() ->
    case mnesia:read({foo, Bar}) of
      [#foo{baz=Baz}] -> Baz;
      [] -> undefined
    end
  end,
  Pid = self(),
  Ref = make_ref(),
  Get = fun() ->
    R = mnesia:activity(transaction, F),
    Pid ! {Ref,baz,R}
  end,
  spawn(Get),
  Ref. %% the function return immediately a ref, and will send later the message {Ref,baz,Baz}.