Activemq 启动分析
Active的启动是从org.apache.activemq.console中的Main类中启动的。
启动中的命令模式
我这里传递参数如下:
-Xms1G -Xmx1G
-Djava.util.logging.config.file=logging.properties
-Dhawtio.realm=activemq
-Dhawtio.role=admins
-Dhawtio.rolePrincipalClasses=org.apache.activemq.jaas.GroupPrincipal
-Dactivemq.home=F:\消息中间件\activeMQ\apache-activemq-5.8.02
-Dactivemq.base=F:\消息中间件\activeMQ\apache-activemq-5.8.02
-Dactivemq.conf=F:\消息中间件\activeMQ\apache-activemq-5.8.02\conf
-Dactivemq.data=F:\消息中间件\activeMQ\apache-activemq-5.8.02\data
-Djava.io.tmpdir=F:\消息中间件\activeMQ\apache-activemq-5.8.02\data\tmpstart
其中,app.runTaskClass(tokens);为main类调用命令模式,通过反射执行 shellCommand
String[] args = tokens.toArray(new String[tokens.size()]);
Class<?> task = cl.loadClass(TASK_DEFAULT_CLASS);
Method runTask = task.getMethod("main", new Class[] {
String[].class, InputStream.class, PrintStream.class
});
runTask.invoke(task.newInstance(),args, System.in, System.out);
这里除了可以使用start命令之外,还有很多其他的命令可以使用:
在shellCommand中,有这样一个方法:
ArrayList<Command>getCommands() {
ServiceLoader<Command> loader =ServiceLoader.load(Command.class);
ArrayList<Command> rc = newArrayList<Command>();
for( Command command: loader ) {
rc.add(command);
}
return rc;
}
注意这个方法中的ServiceLoader.load(Command.class) 这是jdk提供的一个方法,用户加载实现了command接口的所有的实现类。
这里因为传递的是start,所以,会执行对应的 startCommand:
command.execute(tokens);
额,下面才是真正的启动的干货了。
Broker的组装
在执行startCommand中的runTask 方法的时候,会传递进入一个brokerURI数组。该数组就是activemq的核心配置文件。如果该数组为空,则会构建一个xbean:activemq.xml。否则取数组中的第一个为配置文件路径。
然后将URI传入brokerFactory中,并通过brokerFactory创建一个broker,并启动该broker。
broker = BrokerFactory.createBroker(configURI);
broker.start();
那我们就来看看如何构建一个broker的。
BrokerFactoryHandler handler = createBrokerFactoryHandler(brokerURI.getScheme());
BrokerService broker = handler.createBroker(brokerURI);
该方法返回值BrokerFactoryHandler 只是一个接口而已。他的实现类有PropertiesBrokerFactory ,DefaultBrokerFactory等,这里具体的实例到底是个什么类型的呢?
首先,根据brokerURI文件路径,判断出来是那种类型的配置文件。比如我们这里传入的brokerURI是xbean:activemq.xml
这里的加载方式比较有意思。主要加载方式是在org.apache.activemq.util中的FactoryFinder中实现的。
该factoryFinder中,有一个Map,维护了一个类表示和类之间的对应关系。
这里传递进来的是类标志,然后通过标志去加载对应的配置文件:META-INF/services/org/apache/activemq/broker/xbean 。
加载该类之后,也就通过反射,构建出了一个xbeanBrokerFactory了。
所以,构造broker的任务也是由 xbeanBrokerFactory 来通过下面方法完成的
BrokerService broker =handler.createBroker(brokerURI);
其实activemq中,不少的配置都是借鉴了spring ioc思想的,所以xbean也是基于spring ioc构建。该类存在于activemq-spring jar中。
我们来看具体的代码:
ApplicationContext context =createApplicationContext(uri);
BrokerService broker = null;
try {
broker = (BrokerService) context.getBean("broker");
} catch (BeansException e) {
}
if (broker == null) {
// lets try findby type
String[] names = context.getBeanNamesForType(BrokerService.class);
for (int i = 0; i < names.length; i++) {
String name = names[i];
broker = (BrokerService) context.getBean(name);
if (broker != null) {
break;
}
}
}
if (broker == null) {
throw new IllegalArgumentException(
"Theconfiguration has no BrokerService instance for resource: "
+ config);
}
SpringBrokerContext springBrokerContext = newSpringBrokerContext();
springBrokerContext.setApplicationContext(context);
springBrokerContext.setConfigurationUrl(uri);
broker.setBrokerContext(springBrokerContext);
前面几行代码非常的简单。其实就是spring ioc的基本的应用。通过配置文件,或得到broker bean。
Spring 和mq整合部分:
这里不是重点,简单写一些:
在activemq.xml的配置文件中,有一下两个地方需要注意:
其中xsd是用于规范xml里面的内容和格式的。
下面的core就是每一个节点的意义了。
加载路径其实就是在jar包中的。对应路径如下:
其中,标红的哪一行就是说明了当有一个broker的时候,应该生成一个什么样的对象。
那么通过如上的分析,broker的结构就非常的清晰了。
BrokerService的启动
这里分成两部分处理,一部分是数据库持久化适配器的初始化
startPersistenceAdapter(startAsync);
还有一部分是broker 的启动 doStartBroker()
启动持久化适配器
doStartPersistenceAdapter()
在brokerService中,有个PersistenceAdapter属性
持久化适配器的启动就是通过persistenceAdapter的启动来完成的。这里,我以JDBCpersistenceAdapter为例分析一下;
JDBCPersistenceAdapter 继承 DataSourceServiceSupport 并实现了 PersistenceAdapter 接口。其中,persistenceAdapter主要是定义了同数据存储层的操作行为。DataSourceServiceSupport 主要是提供了一个数据源,并且因为是一个service,所以有启动,终止等生命周期。
我们来看一下他的启动,启动主要是通过下面两个方法完成的
preStart():主要完成了对数据库的锁定
doStart():完成设置定期清理任务和构建消息审计
启动broker
主要是通过doStartBroker() 方法启动brokerService的。其中,启动brokerService包括启动所有终端,启动连接器等等。
首先,需要构建一个broker
regionBroker = new RegionBroker(this,getTaskRunnerFactory(), getConsumerSystemUsage(), destinationFactory,
destinationInterceptor,getScheduler(),getExecutor());
开启所有的连接器
startAllConnectors
启动某一个连接器
startTransportConnector
启动完毕之后,加入停止监听.注意这里的conuntDownLatch。他是juc中的一个线程控制类。在完成其他线程的任务之前,他可以一直等待。
final CountDownLatch shutdownLatch = new CountDownLatch(1);
final Thread jvmShutdownHook = new Thread() {
public void run() {
try {
broker.stop();
} catch (Exception e) {
}
}
};
Runtime.getRuntime().addShutdownHook(jvmShutdownHook);
broker.addShutdownHook(new Runnable() {
public void run() {
shutdownLatch.countDown();
}
});
// The broker has stopped..
shutdownLatch.await();
这里需要注意的是Runtime.getRuntime().addShutdownHook(jvmShutdownHook);
这里是在虚拟机中增加了一个关闭钩子。要关闭虚拟机的时候,这些钩子必须先关闭之后,jvm才能够关闭。并且,在brokerService中也增加了一个关闭钩子。BrokerService关闭的时候,计数器shutdownLatch会减少。从而使得shutdownLatch 停止等待,程序退出。