我想将一些参数传递给supervisor:init/1函数,并且希望应用程序的界面如下所示:
redis_pool:start() % start all instances
redis_pool:start(Names) % start only given instances
Run Code Online (Sandbox Code Playgroud)
这是应用程序:
-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).
Run Code Online (Sandbox Code Playgroud)
这是主管:
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}}.
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,当前的实现很丑陋并且可能存在错误。问题是我如何传递一些参数并启动应用程序和主管(使用给予的参数start/1)?
一种选择是启动应用程序并分两个单独的阶段运行 Redis 池。
redis_pool:start(),
redis_pool:run([] | Names).
Run Code Online (Sandbox Code Playgroud)
但是,如果我想在应用程序启动时运行主管子级(redis 池)怎么办?
谢谢。
应用程序回调Module:start/2不是为了启动应用程序而调用的 API。当应用程序启动时调用它application:start/1,2。这意味着重载它来提供不同的参数可能是错误的做法。
特别是,如果有人将您的应用程序添加为他们的应用程序的依赖项(在文件中),application:start将直接调用。foo.app此时,他们无法控制参数,因为它们来自您的 .app文件{mod, {Mod, Args}}。
一些可能的解决方案:
要求参数位于应用程序配置文件中;您可以使用 检索它们application:get_env/2,3。
这意味着以下两件事之一:成为图书馆应用程序({mod, Mod}从.app文件中删除该术语)——您不需要application行为;或者启动一个什么也不做的虚拟主管。
然后,当有人想要使用您的库时,他们可以调用 API 来创建池监督程序,并将其移植到他们的监督树中。这就是 的poolboy作用poolboy:child_spec。
或者,您的应用程序级主管可以是普通主管,默认情况下没有子级,并且您可以提供一个 API 来通过supervisor:start_child. 这(或多或少)就是这样cowboy的。