gpt4 book ai didi

Erlang:如何在主管中正确调度带有 start_child 的 gen_server 并调用 API

转载 作者:行者123 更新时间:2023-12-02 03:12:14 25 4
gpt4 key购买 nike

我的 cavv 应用程序中有一个 gen_server,我需要先启动它才能执行调用。为此,我想使用命令调度程序。举个简短​​的例子,这是 gen_server 的 API:

一个 gen_server: cavv_user

-module(cavv_user).

-behavior(gen_server).

-define(SERVER(UserId), {via, gproc, {n, l, {?MODULE, UserId}}}).

start_link(UserId) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId], []).

change_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER(AggregateId), {execute_command, #change_user_email_address{user_id=UserId, email_address=EmailAddress}}).

在调用 cavv_user:change_email_address() 之前。 我需要启动 cavv_user。我这样做是作为主管中的 simple_one_for_one child ,如下所示:

主管:cavv_user_sup

-module(cavv_user_sup).

-behaviour(supervisor).

-define(CHILD(ChildName, Type, Args), {ChildName, {ChildName, start_link, Args}, temporary, 5000, Type, [ChildName]}).

start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).

start_child(UserId) ->
supervisor:start_child(?SERVER, [UserId]).

init([]) ->
RestartStrategy = {simple_one_for_one, 1, 5},

Children = [?CHILD(cavv_user, worker, [])],

{ok, { RestartStrategy, Children} }.

我现在面临的问题是如何分发命令给cavv_user。我想确保首先使用 start_child 启动正确的用户,然后调用 cavv_user:change_email_address()。

我找到了这个 anwser,使用 dispatcher:Erlang: what supervision tree should I end with writing a task scheduler?

所以我创建了一个命令调度程序,最后得到一个 cavv_user_dispatcher 和一个 cavv_user_dispatcher_sup,后者又包含 cavv_user_dispatcher 和更早的 cavv_user_sup:

         cavv_user_dispatch_sup
| |
cavv_user_dispatcher |
(gen_server) |
|
|
cavv_user_sup
| | |
cavv_user_1...cavv_user_N

cavv_user_dispatcher

这很好用。

我现在面临的问题是,如何正确编写cavv_user_dispatcher中的代码?我面临代码重复的问题。如何正确调用start_child和调用cavv_user合适的API?

我应该像这样使用某种Fun吗?

-module(cavv_user_dispatcher).

dispatch_command(UserId, Fun) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun}}).

handle_call({dispatch_command, {UserId, Fun}}, _From, State) ->
cavv_user_sup:start_child(UserId),
Fun(), %% How to pass: cavv_user:change_email_address(..,..)?
{reply, ok, State};

或者像这样复制 cavv_user 的 API?

-module(cavv_user_dispatcher).

change_user_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER, {change_user_email_address, {UserId, EmailAddress}}).

handle_call({change_user_email_address, {UserId, EmailAddress}}, _From, State) ->
cavv_user_sup:start_child(UserId),
cavv_user:change_email_address(UserId, EmailAddress),
{reply, ok, State};

或者我是否应该将 cavv_user 中的命令记录重新使用到某种实用程序中以正确构建它们并传递它们?也许有更好的方法来传递我想在 cavv_user 调用的函数?

我想尽可能以最好的 Erlang 方式解决问题,避免代码重复。

最佳答案

您的调度员是否应该处理其他命令?

  • 如果是那么下一个命令将如何到来,我的意思是请求者是否知道用户的进程 pid?

    • 如果是,那么您需要 2 个函数,一个用于创建用户,它将 pid 返回给请求者以供下次调用,另一个用于通过向给定的 pid 发送命令来处理下一个请求

    • 如果不是,那么您还需要 2 个函数,一个用于创建用户并将 user_id 与用户进程 pid 一起存储,一个用于通过检索进程 pid 处理下一个请求,然后将命令转发给它 (我想这就是你想要做的)。

  • 如果不是,那么您不需要处理任何命令,并且应该在创建用户进程时直接传递电子邮件地址。请注意,这适用于所有情况,因为您需要不同的界面来创建用户。

我会以这种方式修改您的代码(未经测试,为时已晚:o)!)

-module(cavv_user_dispatcher).

create_user(UserId,UserMail) ->
gen_server:call(?SERVER,{new_user,UserId,UserMail}).

% Args is a list of argument, empty if
% F needs only one argument (the user Pid)
dispatch_command(UserId, Fun, Args) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun,Args}}).

handle_call({dispatch_command, {UserId, Fun,Args}}, _From, State) ->
Pid = get_pid(UserId,State),
Answer = case Pid of
unknown_user_id -> unknown_user_id;
_ -> apply(Fun,[Pid|Args]),
ok
end,
{reply, Answer, State};
handle_call({new_user,UserId,UserMail},_From,State) ->
% verify that the user id does not already exists
CheckId = check_id(UserId,State),
{Answer,NewState} = case CheckId of
false -> {already_exist,State};
true -> {ok,Pid} = cavv_user_sup:start_child(UserId,UserMail)
{ok,[{UserId,Pid}|State]}
% State must be initialized as an empty list in the init function.
{reply, Answer, NewState};
...
get_pid(UserId,State) ->
proplists:get_value(UserId, State, unknown_user_id).
check_id(UserId,State) ->
not proplists:is_defined(UserId, State).

并且必须这样修改用户主管:

start_child(UserId,UserMail) -> % change arity in the export
supervisor:start_child(?SERVER, [UserId,UserMail]).

然后是用户服务器:

start_link(UserId,UserMail) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId,UserMail],[]).

init([UserId,UserMail]) ->
{ok,[{user_id,UserId},{user_mail,UserMail}]}.

关于Erlang:如何在主管中正确调度带有 start_child 的 gen_server 并调用 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39297230/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com