之前提到[链接]RabbitMQ会把启动过程分成若干阶段,按照依赖关系进行启动.rabbit_boot_step里面的requires 和 enables配置节是描述这种依赖关系的关键.require约定了该项目启动所依赖的前置条件,enables 表示当前项目启动之后可以启动什么;如果有多个项目enable一个项目(比如:external_infrastructure),要等这些项目都启动了external_infrastructure 才可以启动.
为什么要搞得那么复杂?一股脑调用启动不就行了?RabbitMQ启动过程中复杂的依赖关系,以及灵活扩展的要求,不可能随机顺序启动或者硬编码启动顺序.官方在GitHub上面举了一个例子:
Boot steps can be separated into groups. A group of boot steps will enabled certain other group. For example routing_ready is actually enabled by many others boot steps, not just recovery. One of such steps is the empty_db_check that ensures that the Mnesia, Erlang's built-in distributed database, has the default data, like the default guest user for example. Also we can see that the recovery boot step also depends on empty_db_check so this logic takes care of running them in the right order that will satisfy the interdependencies they have.
看一下上面所说的图形效果,这只是RabbitMQ整个启动过程的片段,[ 完整高清大图请点击这里 ]:
整个启动过程,会被构造成一个有向无循环图,这里可以参考rabbit.erl的sort_boot_step方法
sort_boot_steps(UnsortedSteps) -> case rabbit_misc:build_acyclic_graph(fun vertices/2, fun edges/2, UnsortedSteps) of {ok, G} -> %% Use topological sort to find a consistent ordering (if %% there is one, otherwise fail). SortedSteps = lists:reverse( [begin {StepName, Step} = digraph:vertex(G, StepName), Step end || StepName <- digraph_utils:topsort(G)]), digraph:delete(G), %% Check that all mentioned {M,F,A} triples are exported. case [{StepName, {M,F,A}} || {StepName, Attributes} <- SortedSteps, {mfa, {M,F,A}} <- Attributes, not erlang:function_exported(M, F, length(A))] of [] -> SortedSteps; MissingFunctions -> basic_boot_error( {missing_functions, MissingFunctions}, "Boot step functions not exported: ~p~n", [MissingFunctions]) end; {error, {vertex, duplicate, StepName}} -> basic_boot_error({duplicate_boot_step, StepName}, "Duplicate boot step name: ~w~n", [StepName]); {error, {edge, Reason, From, To}} -> basic_boot_error( {invalid_boot_step_dependency, From, To}, "Could not add boot step dependency of ~w on ~w:~n~s", [To, From, case Reason of {bad_vertex, V} -> io_lib:format("Boot step not registered: ~w~n", [V]); {bad_edge, [First | Rest]} -> [io_lib:format("Cyclic dependency: ~w", [First]), [io_lib:format(" depends on ~w", [Next]) || Next <- Rest], io_lib:format(" depends on ~w~n", [First])] end]) end.
DEMO
看一个具体例子,RabbitMQ in Action 一书中rabbit_exchange_type_recent_history扩展.代码url: https://github.com/rabbitinaction/sourcecode 看看它的启动规格说明:
-rabbit_boot_step({?MODULE, [{description, "exchange type x-recent-history"}, {mfa, {rabbit_registry, register, [exchange, <<"x-recent-history">>, ?MODULE]}}, {requires, rabbit_registry}, {enables, kernel_ready}]}). -rabbit_boot_step({rabbit_exchange_type_recent_history_mnesia, [{description, "recent history exchange type: mnesia"}, {mfa, {?MODULE, setup_schema, []}}, {requires, database}, {enables, external_infrastructure}]}).
参照上面的高清大图,可以看一下它会在什么位置完成上面两步操作.下面我们重新编译启动Rabbit,看下启动过程的输出,注意新增的部分:
Eshell V5.9 (abort with ^G) (rabbit@nimbus)1> +---+ +---+ | | | | | | | | | | | | | +---+ +-------+ | | | RabbitMQ +---+ | | | | | | v3.0.0 +---+ | | | +-------------------+ AMQP 0-9-1 / 0-9 / 0-8 Copyright (C) 2007-2012 VMware, Inc. Licensed under the MPL. See http://www.rabbitmq.com/ node : rabbit@nimbus app descriptor : /data/rabbitmq-server-3.0.0/scripts/../ebin/rabbit.app home dir : /root config file(s) : (none) cookie hash : qhI/+VnjqUPUYcXLZ2jZMQ== log : /var/log/rabbitmq/rabbit@nimbus.log sasl log : /var/log/rabbitmq/rabbit@nimbus-sasl.log database dir : /var/lib/rabbitmq/mnesia/rabbit@nimbus erlang version : 5.9 -- rabbit boot start starting file handle cache server ...done starting worker pool ...done starting database ...done starting database sync ...done starting codec correctness check ...done starting recent history exchange type: mnesia ...done %%% 看这里 看这里 -- external infrastructure ready starting statistics event manager ...done starting logging server ...done starting plugin registry ...done starting auth mechanism amqplain ...done starting auth mechanism cr-demo ...done starting auth mechanism plain ...done starting exchange type direct ...done starting exchange type fanout ...done starting exchange type headers ...done starting exchange type x-recent-history ...done %%%% 看这里 看这里 starting exchange type topic ...done -- kernel ready starting node monitor ...done starting cluster delegate ...done starting guid generator ...done starting alarm handler ...done starting memory monitor ...done -- core initialized starting empty DB check ...done starting background garbage collection ...done starting HA policy validation ...done starting policy parameters ...done starting exchange, queue and binding recovery ...done starting mirror queue slave sup ...done starting adding mirrors to queues ...done -- message delivery logic ready starting error log relay ...done starting networking ...done starting notify cluster nodes ...done starting direct client ...done broker running
感兴趣的话,可以输出rabbit:boot_steps()看一下,内容略长,请展开:
Boot Step Dump
[{pre_boot,[{description,"rabbit boot start"}]}, {file_handle_cache, [{description,"file handle cache server"}, {mfa,{rabbit,start_fhc,[]}}, {requires,pre_boot}, {enables,worker_pool}]}, {worker_pool, [{description,"worker pool"}, {mfa,{rabbit_sup,start_supervisor_child,[worker_pool_sup]}}, {requires,pre_boot}, {enables,external_infrastructure}]}, {database, [{mfa,{rabbit_mnesia,init,[]}}, {requires,file_handle_cache}, {enables,external_infrastructure}]}, {database_sync, [{description,"database sync"}, {mfa,{rabbit_sup,start_child,[mnesia_sync]}}, {requires,database}, {enables,external_infrastructure}]}, {codec_correctness_check, [{description,"codec correctness check"}, {mfa,{rabbit_binary_generator,check_empty_frame_size,[]}}, {requires,pre_boot}, {enables,external_infrastructure}]}, {rabbit_exchange_type_recent_history_mnesia, [{description,"recent history exchange type: mnesia"}, {mfa,{rabbit_exchange_type_recent_history,setup_schema,[]}}, {requires,database}, {enables,external_infrastructure}]}, {external_infrastructure,[{description,"external infrastructure ready"}]}, {rabbit_event, [{description,"statistics event manager"}, {mfa,{rabbit_sup,start_restartable_child,[rabbit_event]}}, {requires,external_infrastructure}, {enables,kernel_ready}]}, {rabbit_log, [{description,"logging server"}, {mfa,{rabbit_sup,start_restartable_child,[rabbit_log]}}, {requires,external_infrastructure}, {enables,kernel_ready}]}, {rabbit_registry, [{description,"plugin registry"}, {mfa,{rabbit_sup,start_child,[rabbit_registry]}}, {requires,external_infrastructure}, {enables,kernel_ready}]}, {rabbit_auth_mechanism_amqplain, [{description,"auth mechanism amqplain"}, {mfa, {rabbit_registry,register, [auth_mechanism,<<"AMQPLAIN">>,rabbit_auth_mechanism_amqplain]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_auth_mechanism_cr_demo, [{description,"auth mechanism cr-demo"}, {mfa, {rabbit_registry,register, [auth_mechanism,<<"RABBIT-CR-DEMO">>, rabbit_auth_mechanism_cr_demo]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_auth_mechanism_plain, [{description,"auth mechanism plain"}, {mfa, {rabbit_registry,register, [auth_mechanism,<<"PLAIN">>,rabbit_auth_mechanism_plain]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_exchange_type_direct, [{description,"exchange type direct"}, {mfa, {rabbit_registry,register, [exchange,<<"direct">>,rabbit_exchange_type_direct]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_exchange_type_fanout, [{description,"exchange type fanout"}, {mfa, {rabbit_registry,register, [exchange,<<"fanout">>,rabbit_exchange_type_fanout]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_exchange_type_headers, [{description,"exchange type headers"}, {mfa, {rabbit_registry,register, [exchange,<<"headers">>,rabbit_exchange_type_headers]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_exchange_type_recent_history, [{description,"exchange type x-recent-history"}, {mfa, {rabbit_registry,register, [exchange,<<"x-recent-history">>, rabbit_exchange_type_recent_history]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {rabbit_exchange_type_topic, [{description,"exchange type topic"}, {mfa, {rabbit_registry,register, [exchange,<<"topic">>,rabbit_exchange_type_topic]}}, {requires,rabbit_registry}, {enables,kernel_ready}]}, {kernel_ready, [{description,"kernel ready"},{requires,external_infrastructure}]}, {rabbit_node_monitor, [{description,"node monitor"}, {mfa,{rabbit_sup,start_restartable_child,[rabbit_node_monitor]}}, {requires,kernel_ready}, {enables,core_initialized}]}, {delegate_sup, [{description,"cluster delegate"}, {mfa,{rabbit,boot_delegate,[]}}, {requires,kernel_ready}, {enables,core_initialized}]}, {guid_generator, [{description,"guid generator"}, {mfa,{rabbit_sup,start_restartable_child,[rabbit_guid]}}, {requires,kernel_ready}, {enables,core_initialized}]}, {rabbit_alarm, [{description,"alarm handler"}, {mfa,{rabbit_alarm,start,[]}}, {requires,kernel_ready}, {enables,core_initialized}]}, {rabbit_memory_monitor, [{description,"memory monitor"}, {mfa,{rabbit_sup,start_restartable_child,[rabbit_memory_monitor]}}, {requires,rabbit_alarm}, {enables,core_initialized}]}, {core_initialized,[{description,"core initialized"},{requires,kernel_ready}]}, {empty_db_check, [{description,"empty DB check"}, {mfa,{rabbit,maybe_insert_default_data,[]}}, {requires,core_initialized}, {enables,routing_ready}]}, {background_gc, [{description,"background garbage collection"}, {mfa,{rabbit_sup,start_restartable_child,[background_gc]}}, {enables,networking}]}, {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_policy, [{description,"policy parameters"}, {mfa,{rabbit_policy,register,[]}}, {requires,rabbit_registry}, {enables,recovery}]}, {recovery, [{description,"exchange, queue and binding recovery"}, {mfa,{rabbit,recover,[]}}, {requires,core_initialized}, {enables,routing_ready}]}, {mirror_queue_slave_sup, [{description,"mirror queue slave sup"}, {mfa, {rabbit_sup,start_supervisor_child,[rabbit_mirror_queue_slave_sup]}}, {requires,recovery}, {enables,routing_ready}]}, {mirrored_queues, [{description,"adding mirrors to queues"}, {mfa,{rabbit_mirror_queue_misc,on_node_up,[]}}, {requires,mirror_queue_slave_sup}, {enables,routing_ready}]}, {routing_ready, [{description,"message delivery logic ready"}, {requires,core_initialized}]}, {log_relay, [{description,"error log relay"}, {mfa,{rabbit_error_logger,boot,[]}}, {requires,routing_ready}, {enables,networking}]}, {networking,[{mfa,{rabbit_networking,boot,[]}},{requires,log_relay}]}, {notify_cluster, [{description,"notify cluster nodes"}, {mfa,{rabbit_node_monitor,notify_node_up,[]}}, {requires,networking}]}, {direct_client, [{description,"direct client"}, {mfa,{rabbit_direct,boot,[]}}, {requires,log_relay}]}]
最后小图一张:迅哥,周末愉快!