【问题标题】:Pass some arguments to supervisor init function when app starts应用程序启动时将一些参数传递给主管初始化函数
【发布时间】:2014-11-26 21:06:29
【问题描述】:

我想将一些参数传递给supervisor:init/1 函数,并且希望应用程序的界面是这样的:

redis_pool:start() % start all instances
redis_pool:start(Names) % start only given instances

这是应用程序:

-module(redis_pool).
-behaviour(application).

...

start() -> % start without params
    application:ensure_started(?APP_NAME, transient).

start(Names) -> % start with some params
    % I want to pass Names to supervisor init function
    % in order to do that I have to bypass application:ensure_started
    % which is not GOOD :(
    application:load(?APP_NAME),
    case start(normal, [Names]) of
        {ok, _Pid} -> ok;
        {error, {already_started, _Pid}} -> ok
    end.

start(_StartType, StartArgs) ->
    redis_pool_sup:start_link(StartArgs).

这里是主管:

init([]) ->
    {ok, Config} = get_config(),
    Names = proplists:get_keys(Config),
    init([Names]);
init([Names]) ->
    {ok, Config} = get_config(),
    PoolSpecs = lists:map(fun(Name) ->
        PoolName = pool_utils:name_for(Name),
        {[Host, Port, Db], PoolSize} = proplists:get_value(Name, Config),
        PoolArgs = [{name, {local, PoolName}},
                {worker_module, eredis},
                {size, PoolSize},
                {max_overflow, 0}],
        poolboy:child_spec(PoolName, PoolArgs, [Host, Port, Db])
    end, Names),
    {ok, {{one_for_one, 10000, 1}, PoolSpecs}}.

如您所见,当前的实现很丑陋并且可能存在错误。问题是我如何传递一些参数并启动应用程序和主管(使用给 start/1 的参数)?

一个选项是在两个单独的阶段启动应用程序和运行 redis 池。

redis_pool:start(),
redis_pool:run([] | Names).

但是如果我想在我的应用启动时运行主管子级(redis 池)怎么办?

谢谢。

【问题讨论】:

    标签: erlang erlang-otp


    【解决方案1】:

    应用程序回调Module:start/2 不是为了启动应用程序而调用的 API。当应用程序由application:start/1,2 启动时调用。这意味着重载它以提供不同的参数可能是错误的做法。

    特别是,如果有人将您的应用程序添加为他们的依赖项(在foo.app 文件中),则将直接调用application:start。此时,他们没有控制参数,因为它们来自你的.app文件,在{mod, {Mod, Args}}术语中。

    一些可能的解决方案:

    应用程序配置文件

    要求参数在应用程序配置文件中;您可以使用application:get_env/2,3 检索它们。

    不要启动主管

    这意味着两件事之一:成为库应用程序(从您的 .app 文件中删除 {mod, Mod} 术语)——您不需要 application 行为;或者启动一个什么都不做的虚拟主管。

    然后,当有人想要使用您的库时,他们可以调用 API 来创建池监督器,并将其移植到他们的监督树中。这就是poolboypoolboy:child_spec 所做的。

    或者,您的应用程序级主管可以是普通主管,默认情况下没有子级,您可以通过supervisor:start_child 提供一个 API 来启动其子级。这(或多或少)是cowboy 所做的。

    【讨论】:

    • 谢谢罗杰的详细解释!对于那些阅读本文的人,我最终以 0 个孩子开始主管并拥有 run(Names) & run_all 函数,这些函数将孩子添加到主管。
    【解决方案2】:

    您可以根据文档将 AppDescr 参数中的参数传递给 application:load/1 (尽管它已经是一个强大的大元组......)作为 {mod, {Module, StartArgs}} (“根据文档”,我不记得自己曾经这样做过:http://www.erlang.org/doc/apps/kernel/application.html#load-1)。

    application:load({application, some_app, {mod, {Module, [Stuff]}}})
    

    在不了解您正在启动的应用程序的内部情况的情况下,很难说哪种方式最好,但一种常见的方法是启动应用程序,然后向它发送一条包含您想要的数据的消息它知道。

    您可以接收消息表单告诉应用程序通过配置断言过程,这样您在启动时发送的相同消息也是您发送它以动态重新配置它的相同类型的东西。我发现这比启动时的一次性参数更有用。

    在任何情况下,通常最好先考虑开始某事,然后要求它为您做某事,而不是尝试在 init 参数中告诉它所有内容。这可以像让它启动并等待一些消息一样简单RL 在他的回答中提到的问题。

    【讨论】:

      猜你喜欢
      • 2014-05-24
      • 1970-01-01
      • 1970-01-01
      • 2020-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多