本篇文章主要讲解了ContainerBase类中的一些重要方法
在之前的文章中我们说过Catalina
中包含4种容器,分别是Wrapper
,Context
,Host
,Engine
。我们今天来介绍下相关的CatainerBase
类,先看类图。
从图中可以看到Catalina
中4中容器都继承了一个共同的接口Container
,Container
的抽象实现类是ContainerBase
,而4种容器的标准实现分别是StandardEngine
,StandardHost
,StandardContext
,StandardWrapper
。关于4个标准实现类我们会在后面关于Catalina
处理请求的时候讲,今天我们主要查看ContainerBase
类。ContainerBase
类中方法比较多,就不直接贴源码了,我们挑部分来查看。大家都知道所有的容器组件都有自己的生命周期过程,那么在这个过程中都会调用ContainerBase
类的init()
,start()
,stop()
,destroy()
方法,而实际上ContainerBase
也继承了LifecycleBase
类,所以是没有init()
这种方法,而是只需要实现initInternal()
方法。鉴于我们之前在启动过程5
的最后一部分非常粗略的提到过ContainerBase
类的startInternal()
方法,所以我们今天在这里将详细讲解initInternal()
和startInternal()
这2个方法。
initInternal
protected ThreadPoolExecutor startStopExecutor;
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<Runnable>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
线程池构造
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
其实initInternal()
方法比较简单,就是初始化了startStopExecutor
这个成员变量,而这个变量指向的是一个线程池,追踪下getStartStopThreadsInternal()
可以看出默认返回1,所以这个线程池corePoolSize
,maximumPoolsize
都是1,缓存时间是10秒。再看super.initInternal()
方法则是在LifecycleMBeanBase
类中注册JMX,跳过开始看startInternal()
方法。
startInternal
/**
* Start this component and implement the requirements
* of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected synchronized void startInternal() throws LifecycleException {
// Start our subordinate components, if any
//111
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start();
logger = null;
getLogger();
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
Realm realm = getRealmInternal();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
//2222222222222
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<Future<Void>>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
boolean fail = false;
for (Future<Void> result : results) {
try {
result.get();
} catch (Exception e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
fail = true;
}
}
if (fail) {
throw new LifecycleException(
sm.getString("containerBase.threadedStartFailed"));
}
//333333333
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
setState(LifecycleState.STARTING);
//444444
// Start our thread
threadStart();
}
代码1的地方是启动各个组件,每个容器的组件不需要分别启动,而是在模版方法中统一启动。
代码2,先看findChildren()
方法
/**
* Return the set of children Containers associated with this Container.
* If this Container has no children, a zero-length array is returned.
*/
@Override
public Container[] findChildren() {
synchronized (children) {
Container results[] = new Container[children.size()];
return children.values().toArray(results);
}
}
返回和本容器相关的所有的子容器的集合,比如Engine
对应的子容器当然是Host
,Host
对应的是Context
等。这里使用了Future
模式,先创建一个任务的集合,然后把所有任务添加到这个集合里(线程池的submit()
方法返回任务本身),最后遍历任务集合调用每个task的get()
方法来获取任务执行结果,剩下的只要看下StartChild
类即可。
private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}
静态内部类,实现Callable
,在call()
方法中调用了子容器的start()
方法。看到这里就比较明了了,当一个容器在启动(调用start()
)方法的时候,他的子容器的启动是采用向线程池提交任务的方式来完成的。
在任务提交完毕后,调用task的get()
方法来保证所有子容器start()
方法调用完毕线程才继续网下执行(get()
方法是阻塞的)。
代码3的地方是调用自身容器的pipeline
组件的start()
方法。
代码4的地方调用了一个私有方法threadStart()
,我们来看下源码。
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart() {
//thread
if (thread != null)
return;
if (backgroundProcessorDelay <= 0)
return;
threadDone = false;
String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
thread = new Thread(new ContainerBackgroundProcessor(), threadName);
thread.setDaemon(true);
thread.start();
}
/**
* The background thread.
*/
private Thread thread = null;
看起来很简单,其实这个私有方法是start()
方法中最重要的方法,也是今天这篇文章讲解的重点。
第一步thread
变量的判断,这个没啥,只是非空判断,没啥继续往下看。
如果backgroundProcessorDelay
不大于0,那么方法就停止。那么这个变量的值是多少呢。
/**
* The processor delay for this component.
*/
protected int backgroundProcessorDelay = -1;
可以看到默认初始化是-1,但是不同的容器的值可能不一样,我们去标准的4个容器中查看下。
StandardEngine
/**
* Create a new StandardEngine component with the default basic Valve.
*/
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System.getProperty("jvmRoute"));
} catch(Exception ex) {
log.warn(sm.getString("standardEngine.jvmRouteFail"));
}
// By default, the engine will hold the reloading thread
backgroundProcessorDelay = 10;
}
全查过一遍发现,只有在StandardEngine
中能看到这个变量的赋值,也就是说其他三个容器中这个值默认就是-1,同时也说明只有StandardEngine
在start()
方法调用的时候才会走这个方法,其他容器这个方法是走不到下面代码的。了解了这里我们就来看这个方法到底做了什么吧。
新建了一个线程,传递了一个ContainerBackgroundProcessor
的实例,然后设置线程为守护线程,并且启动线程。ContainerBackgroundProcessor
是个Runnable
接口的实现类,查看其run
方法。
/**
* The background thread completion semaphore.
*/
private volatile boolean threadDone = false;
@Override
public void run() {
Throwable t = null;
String unexpectedDeathMessage = sm.getString(
"containerBase.backgroundProcess.unexpectedThreadDeath",
Thread.currentThread().getName());
try {
while (!threadDone) {
try {
//睡眠一段事件
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
//获取容器本身
Container parent = (Container) getMappingObject();
//获取当前线程的classloader
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
//11111
processChildren(parent, cl);
}
}
} catch (RuntimeException e) {
t = e;
throw e;
} catch (Error e) {
t = e;
throw e;
} finally {
if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
}
}
可以看到threadDone
变量初始化是false
,也就是while循环进去,先睡眠一段时间,然后继续往下执行,先获取容器本身,再获取当前线程的classloader
,传递参数给processChildren
这个方法。
protected void processChildren(Container container, ClassLoader cl) {
try {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
container.backgroundProcess();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("Exception invoking periodic operation: ", t);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
这里传递进来的Container
的实现类其实是StandardEngine
,调用其backgroundProcess
方法
/**
* Execute a periodic task, such as reloading, etc. This method will be
* invoked inside the classloading context of this container. Unexpected
* throwables will be caught and logged.
*/
@Override
public void backgroundProcess() {
if (!getState().isAvailable())
return;
if (cluster != null) {
try {
cluster.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);
}
}
if (loader != null) {
try {
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);
}
}
if (manager != null) {
try {
manager.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.manager", manager), e);
}
}
Realm realm = getRealmInternal();
if (realm != null) {
try {
realm.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.realm", realm), e);
}
}
Valve current = pipeline.getFirst();
while (current != null) {
try {
current.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.valve", current), e);
}
current = current.getNext();
}
fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
}
乍一看感觉很复杂,看不懂,不妨先看看英文注释,方法上的文档说的很清楚:执行一个周期性的任务,比如reloading
等。也许还是有点迷糊,但是主要不懂的地方还是在于不知道这个方法里每个组件(比如cluster,loader,manager等等代表的意义),这里就不详细的介绍了,也许后面有空会仔细介绍。先大致讲解下
cluster
Catalina的集群组件,通过cluster
组件可以实现:集群应用部署,即多个Tomcat实例,不需要每个都分别部署应用,只需要在某个实例上部署,整个集群中的各个实例都会自动同步应用进行部署。那么他的backgroundProcess()
方法主要的功能是监听指定文件夹下有没有新增的war包或者文件是新增的还是修改的已决定来重新部署和通知其他tomcat集群成员。
loader
Catalina在启动过程中创建的classloader
的实例,backgroundProcess()
方法主要的功能是查看Context容器是否需要重新加载,热部署就是利用这个机制来完成的。
manager
Catalina中的Session管理器,backgroundProcess()
方法主要的功能是将过期会话(session)置为无效
realm
Catalina中的安全组件,backgroundProcess()
方法主要的功能是,我也不清楚貌似是跟servlet
的安全校验有关。
pipeline
容器的pipeline
组件,这里是遍历整个pipeline
链表,分别调用backgroundProcess()
方法,不太重要,每个具体类功能也不同。
下面解释下为啥方法这样写。因为在早期tomcat中不同的组件都要做一些周期性的检查,他们都分别在自己的类中启动一个线程来做这些检查,后来就统一写到了一起,一个是方便管理,另外个也是设计模式的体现。
我们继续看processChildren()
方法的后半部分。
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
很简单,获取所有的子容器,然后遍历每个子容器来调用他们的processChildren()
方法。
好了,到这里ContainerBase
类的startInternal()
方法就分析完毕了,可以看到,在角角落落里藏了很多个值得注意的东西,比如说下面就要提到的Session
管理器等,至于stop
,destroy
以及其他方法就留个读者自行探索了。