在.net里面我们可以使用Attribute和反射在运行时完成对程序集元数据的解析; 下面是C#写得一个简单的例子:
Worker1 worker1 = new Worker1 ();
var attribute = worker1.GetType().GetCustomAttribute(typeof( ProcessOrderAttribute)) as ProcessOrderAttribute ;
Console.WriteLine("Description {0} Order {1}" , attribute.Descrption, attribute.Order);
Console.ReadLine();
[ProcessOrder( " first step", 1)]
public class Worker1
{
}
class ProcessOrderAttribute : Attribute
{
public string Descrption { get; set; }
public int Order { get; set; }
public ProcessOrderAttribute(string description, int order)
{
Descrption = description;
Order = order;
}
}
类似的在Erlang中,也可以做类似的事情,我们可以通过module_info获取模块的元数据,比如:
(rabbit@nimbus)4> test:module_info().
[{exports,[{module_info,0},{module_info,1}]},
{imports,[]},
{attributes,[{vsn,[64335248162526234078446821625873662118]},
{tag,[generated_by_tool]}]},
{compile,[{options,[{d,use_specs},
{outdir,"/data/rabbitmq-server-3.0.0/ebin"},
{i,"/data/rabbitmq-server-3.0.0/include"},
debug_info]},
{version,"4.8"},
{time,{2012,12,12,14,39,42}},
{source,"/data/rabbitmq-server-3.0.0/src/test.erl"}]}]
(rabbit@nimbus)5>
在RabbitMQ中就使用元数据
rabbit_boot_step来控制启动的步骤,比如在下面是rabbit.erl中的代码片段,每一个rabbit_boot_step都定义了启动的mfa,前置条件,描述信息等等,在rabbit启动的时候,会根据模块的元素信息按顺序完成启动.
-rabbit_boot_step({rabbit_log,
[{description, "logging server"},
{mfa, {rabbit_sup, start_restartable_child,
[rabbit_log]}},
{requires, external_infrastructure},
{enables, kernel_ready}]}).
-rabbit_boot_step({rabbit_event,
[{description, "statistics event manager"},
{mfa, {rabbit_sup, start_restartable_child,
[rabbit_event]}},
{requires, external_infrastructure},
{enables, kernel_ready}]}).
-rabbit_boot_step({kernel_ready,
[{description, "kernel ready"},
{requires, external_infrastructure}]}).
在rabbit.erl模块可以看到rabbitmq是如何通过解析模块元数据并完成.
start(normal, []) ->
case erts_version_check() of
ok ->
{ok, Vsn} = application:get_key(rabbit, vsn),
error_logger:info_msg("Starting RabbitMQ ~s on Erlang ~s~n",
[Vsn, erlang:system_info(otp_release)]),
{ok, SupPid} = rabbit_sup:start_link(),
true = register(rabbit, self()),
print_banner(),
[ok = run_boot_step(Step) || Step <- boot_steps()],
io:format("~nbroker running~n"),
{ok, SupPid};
Error ->
Error
end.
run_boot_step({StepName, Attributes}) ->
Description = case lists:keysearch(description, 1, Attributes) of
{value, {_, D}} -> D;
false -> StepName
end,
case [MFA || {mfa, MFA} <- Attributes] of
[] ->
io:format("-- ~s~n", [Description]);
MFAs ->
io:format("starting ~-60s ...", [Description]),
[try
apply(M,F,A)
catch
_:Reason -> boot_error(Reason, erlang:get_stacktrace())
end || {M,F,A} <- MFAs],
io:format("done~n"),
ok
end.
boot_steps() ->
sort_boot_steps(rabbit_misc:all_module_attributes(rabbit_boot_step)).
至于rabbit_misc:all_module_attributes的实现,就简单了,遍历加载所有模块的元数据即可:
\rabbitmq-server-3.0.0\src\rabbit_misc.erl
all_module_attributes(Name) ->
Modules =
lists:usort(
lists:append(
[Modules || {App, _, _} <- application:loaded_applications(),
{ok, Modules} <- [application:get_key(App, modules)]])),
lists:foldl(
fun (Module, Acc) ->
case lists:append([Atts || {N, Atts} <- module_attributes(Module),
N =:= Name]) of
[] -> Acc;
Atts -> [{Module, Atts} | Acc]
end
end, [], Modules).
module_attributes(Module) ->
case catch Module:module_info(attributes) of
{'EXIT', {undef, [{Module, module_info, _} | _]}} ->
io:format("WARNING: module ~p not found, so not scanned for boot steps.~n",
[Module]),
[];
{'EXIT', Reason} ->
exit(Reason);
V ->
V
end.
调用的结果样例:
(rabbit@nimbus)5> rabbit_misc:all_module_attributes(rabbit_boot_step).
[{rabbit_policy,[{rabbit_policy,[{description,"policy parameters"},
{mfa,{rabbit_policy,register,[]}},
{requires,rabbit_registry},
{enables,recovery}]}]},
{rabbit_mirror_queue_misc,[{rabbit_mirror_queue_misc,[{description,"HA policy validation"},
{mfa,{rabbit_registry,register,
[policy_validator,<<"ha-mode">>,rabbit_mirror_queue_misc]}},
{mfa,{rabbit_registry,register,
[policy_validator,<<"ha-params">>,
rabbit_mirror_queue_misc]}},
{requires,rabbit_registry},
{enables,recovery}]}]},
{rabbit_exchange_type_topic,[{rabbit_exchange_type_topic,[{description,"exchange type topic"},
{mfa,{rabbit_registry,register,
[exchange,<<"topic">>,rabbit_exchange_type_topic]}},
{requires,rabbit_registry},
........
关乎扩展
如果我们需要编写RabbitMQ的扩展,要让知道rabbit知道我们新定义的模块就需要进行注册,而这个注册就是在模块中添加rabbit_boot_step,在Rabbit启动的适当阶段加入我们的模块,完成注册后在ETS的rabbit_registry表可以看到我们新注册的模块,比如rabbit_exchange_type_recent_history就是我们增加的新exchange.
(rabbit@nimbus)4> ets:i(rabbit_registry).
<1 > {{exchange,'x-recent-history'},rabbit_exchange_type_recent_history}
<2 > {{policy_validator,'ha-mode'},rabbit_mirror_queue_misc}
<3 > {{policy_validator,'ha-params'},rabbit_mirror_queue_misc}
<4 > {{auth_mechanism,'AMQPLAIN'},rabbit_auth_mechanism_amqplain}
<5 > {{exchange,topic},rabbit_exchange_type_topic}
<6 > {{runtime_parameter,policy},rabbit_policy}
<7 > {{auth_mechanism,'PLAIN'},rabbit_auth_mechanism_plain}
<8 > {{exchange,headers},rabbit_exchange_type_headers}
<9 > {{auth_mechanism,'RABBIT-CR-DEMO'},rabbit_auth_mechanism_cr_demo}
<10 > {{exchange,direct},rabbit_exchange_type_direct}
<11 > {{exchange,fanout},rabbit_exchange_type_fanout}
EOT (q)uit (p)Digits (k)ill /
又跟RabbitMQ学了一招,心情大好.
今天北京大雪,不禁想起"六出飞花入户时,坐看青竹变琼枝"的佳句,小图一张(小时候有一件一模一样的棉服):

