0

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

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

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

4

3 回答 3

1

完整的答案在很大程度上取决于您的应用程序——例如,一个gen_server可能就足够了,或者您可能真的需要一个数据库连接池。但是您应该注意的一件事是,agen_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默认超时为 5 秒,因此如果您希望数据库调用的持续时间超过默认值,则需要处理该超时。

于 2014-06-04T23:34:55.317 回答
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 )。

于 2014-06-05T15:16:22.837 回答
0

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

如果你想阅读,2个案例:

  • 您可以在等待答案的同时做其他事情,然后调用 gen_server 就可以了,但是一个简单的衍生进程等待答案并将其发送回客户端将提供相同的服务。

  • 在得到答案之前你不能做任何事情,那么就不存在阻塞问题,而且我认为最好使用尽可能少的代码,这样一个简单的函数调用就足够了。

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}.
于 2014-06-05T20:43:02.013 回答