在erlang中,对于application的配置参数,可以分为三层:
最下层是application的资源文件(resource file),在资源文件中可通过{env,[{Par,Val}]}的形式完成application配置参数的默认设置。
中间层为erlang的配置文件,在配置文件中,可以指定所有应用的配置参数,其文件格式为:
[
{Application1, [{Par11, Val11},
{Par12, Val12}
]},
{Application2, [{Par21, Val21},
{Par22, Val22}
]}
].
程序启动时,通过命令行参数 -config 指定配置文件。
最上层为erlang的命令行参数来指定应用的配置参数。
如果三层对同一应用的某个参数都进行了配置,那么上层会覆盖下层的配置。
我们以一个实例来说明下。
1) Rabbitmq的资源文件(rabbit.app)中,侦听端口默认设置为5672。
2) 配置文件(rabbitmq.config)中侦听端口设置为5675
3) 环境变量配置文件(rabbitmq-env.conf)设置的值通过命令行参数传入
由上面说的不同层次覆盖关系,我们不难知道最终的侦听端口是命令行参数指定的6672。
相关源码:
init进程解析命令行参数并保存:
init.erl
boot(BootArgs) ->
...
{Start0, Flags, Args} = parse_boot_args(BootArgs),
...
parse_boot_args(Args) ->
parse_boot_args(Args, [], [], []).
parse_boot_args( [B|Bs], Ss, Fs, As) ->
case check(B) of
...
flag ->
{F, Rest} = get_args(Bs, []),
Fl = case F of
[] -> [B];
FF -> [B, FF]
end,
parse_boot_args(Rest, Ss, [list_to_tuple(F1)|Fs], As);
...
application加载时完成配置参数的获取与合并:
application_controller.erl
init(Init, Kernel) ->
...
case catch check_conf() of
{ok, ConfData} ->
...
S = #state{conf_data = ConfData}
...
load(S, {ApplData, ApplEnv, IncApps, Descr, Id, Vsn, Apps}) ->
Name = ApplData#appl_data.name,
%% 从 .config 配置文件中获取 application 的配置参数
ConfEnv = get_env_i(Name, S),
%% .config中的配置参数 覆盖 .app 文件中的配置参数
NewEnv = merge_app_env(ApplEnv, ConfEnv),
%% 从命令行参数中获取application的配置参数
CmdLineEnv = get_cmd_env(Name),
%% 命令行参数中的配置参数 覆盖 之前的配置参数
NewEnv2 = merge_app_env(NewEnv, CmdLineEnv),
...