前言
上篇文章分析了Service的 init 和 start 方法,在这两个方法中关键的是调用 Engine 和 Connector 的 init 和 start 方法,一个 Service 里只有一个 Engine,有多个 Connector。本篇文章分析 Engine 的启动。Engine 的实现类是 StandardEngine,
1 StandardEngine#init 方法
StandardEngine 的父类是 ContainerBase ,而 ContainerBase 的父类是 LifecycleMBeanBase。ContainerBase 和 StandardEngine 都实现了 initInternal 和 startInternal 方法。
1.1 StandardEngine#initInternal 方法
@Override
protected void initInternal() throws LifecycleException {
// Ensure that a Realm is present before any attempt is made to start
// one. This will create the default NullRealm if necessary.
getRealm();
super.initInternal();
}
StandardEngine 的 initInternal 方法中,先调用了 getRealm() 方法,确保 StandardEngine 的父类 StandardEngine中的 Realm 类型的属性不为空。
Realm 是 Tomcat 的特性功能,跟 NamingResouce,这里先略过。
然后调用了 super.initInternal(),也就是 ContainerBase 的 initInternal 方法
1.2 ContainerBase#initInternal 方法
@Override
protected void initInternal() throws LifecycleException {
reconfigureStartStopExecutor(getStartStopThreads());
super.initInternal();
}
/**
* The number of threads available to process start and stop events for any
* children associated with this container.
*/
private int startStopThreads = 1;
protected ExecutorService startStopExecutor;
@Override
public int getStartStopThreads() {
return startStopThreads;
}
private void reconfigureStartStopExecutor(int threads) {
if (threads == 1) {
// Use a fake executor
if (!(startStopExecutor instanceof InlineExecutorService)) {
startStopExecutor = new InlineExecutorService();
}
} else {
// Delegate utility execution to the Service
Server server = Container.getService(this).getServer();
server.setUtilityThreads(threads);
startStopExecutor = server.getUtilityExecutor();
}
}
super.initInternal() 是调用的 LifecycleMBeanBase 的 initInternal() 方法。之前的文章里讲过了,这里就略过。
reconfigureStartStopExecutor 方法是设置一个线程池来处理子容器启动和关闭事件,。
可以看出,getStartStopThreads() 返回的是成员变量 startStopThreads,而 startStopThreads 默认为 1 ,所以 reconfigureStartStopExecutor 方法会走 if 语句,而 startStopExecutor 最开始是没有赋值的,startStopExecutor instanceof InlineExecutorService 会返回 false,因此最终会执行 startStopExecutor = new InlineExecutorService(),InlineExecutorService 只是简单地实现了 java.util.concurrent.AbstractExecutorService 类。
最终 reconfigureStartStopExecutor 给 startStopExecutor 这个成员变量设置了,startStopExecutor。
2 StandardEngine#start 方法
StandardEngine 的 start 方法跟它的 init 方法类似。
2.1 StandardEngine#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 {
// Log our server identification information
if (log.isInfoEnabled()) {
log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
}
// Standard container startup
super.startInternal();
}
StandardEngine#startInternal 方法只是简单地调用了 ContainerBase 的startInternal 方法。
2.2 ContainerBase#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
logger = null;
getLogger();
Cluster cluster = getClusterInternal();
if (cluster instanceof Lifecycle) {
((Lifecycle) cluster).start();
}
Realm realm = getRealmInternal();
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Start our child containers, if any
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i++) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
MultiThrowable multiThrowable = null;
for (Future<Void> result : results) {
try {
result.get();
} catch (Throwable e) {
log.error(sm.getString("containerBase.threadedStartFailed"), e);
if (multiThrowable == null) {
multiThrowable = new MultiThrowable();
}
multiThrowable.add(e);
}
}
if (multiThrowable != null) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
multiThrowable.getThrowable());
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
setState(LifecycleState.STARTING);
// Start our thread
if (backgroundProcessorDelay > 0) {
monitorFuture = Container.getService(ContainerBase.this).getServer()
.getUtilityExecutor().scheduleWithFixedDelay(
new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
}
}
首先调用了 getClusterInternal() 和 getRealmInternal() 方法分别获取了,Cluster 和 Realm 对象,然后调用这两个对象的 start 方法啊,如果这两个对象是继承自 Lifecycle 的话。
Cluster 和 Realm 是 tomcat 新增的特性,这里就先略过不讲。
其次,用 findChildren() 方法获取子容器
并将子容器的启动封装在一个 StartChild 对象里,
然后将这个 StartChild 对象丢到 startStopExecutor 中,这个 startStopExecutor 就是在文中上面介绍到的,
并等待执行结果,如果执行中出现异常,则将收集到 MultiThrowable 对象了,并抛出 LifecycleException 异常。
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;
}
}
StartChild 类就只是简单执行 Container 对象的 start 方法。
Engine 的子容器是 Host,而 Host 的子容器是 Context,Context 的子容器是 Wrapper。
所以 Host#start,Context#start,Wrapper#start 会被依次调用。
Host#start,Context#start,Wrapper#start 会在后面的文章中介绍,这里先略过。
调用完子容器的 start 方法之后,就开始调用 Pipeline 的 start 方法。
Pipeline 是 Container 用来处理请求的,上篇文章提到 Container 是处理请求的,Container 处理请求实际上是交给 Pipeline 处理的,Pipeline 串联了一个或多个 Valve,用 Value 去处理请求。
Pipeline 和 Valve 是处理Http请求非常重要的组件,后面的文章会专门讲解。
最后,根据 backgroundProcessorDelay 判断是否需要在后台处理一些任务,如果需要就使用 Server 里的 utilityExecutorWrapper 线程池去执行 ContainerBackgroundProcessorMonitor 任务。utilityExecutorWrapper 在这篇文章中介绍过。
backgroundProcessorDelay 的默认值是 -1,但是在 StandardEngine 里被赋值为 10。
protected class ContainerBackgroundProcessorMonitor implements Runnable {
@Override
public void run() {
if (getState().isAvailable()) {
threadStart();
}
}
}
ContainerBackgroundProcessorMonitor 任务很简单,就是执行 threadStart() 方法。
2.3 ContainerBase#threadStart() 方法
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart() {
if (backgroundProcessorDelay > 0
&& (getState().isAvailable() || LifecycleState.STARTING_PREP.equals(getState()))
&& (backgroundProcessorFuture == null || backgroundProcessorFuture.isDone())) {
if (backgroundProcessorFuture != null && backgroundProcessorFuture.isDone()) {
// There was an error executing the scheduled task, get it and log it
try {
backgroundProcessorFuture.get();
} catch (InterruptedException | ExecutionException e) {
log.error(sm.getString("containerBase.backgroundProcess.error"), e);
}
}
backgroundProcessorFuture = Container.getService(this).getServer().getUtilityExecutor()
.scheduleWithFixedDelay(new ContainerBackgroundProcessor(),
backgroundProcessorDelay, backgroundProcessorDelay,
TimeUnit.SECONDS);
}
}
threadStart() 的重点是使用 Server 里的 utilityExecutorWrapper 去执行 ContainerBackgroundProcessor 任务。
2.4 ContainerBackgroundProcessor的 run 方法
/**
* Private runnable class to invoke the backgroundProcess method
* of this container and its children after a fixed delay.
*/
protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
processChildren(ContainerBase.this);
}
protected void processChildren(Container container) {
ClassLoader originalClassLoader = null;
try {
if (container instanceof Context) {
Loader loader = ((Context) container).getLoader();
// Loader will be null for FailedContext instances
if (loader == null) {
return;
}
// Ensure background processing for Contexts and Wrappers
// is performed under the web app's class loader
originalClassLoader = ((Context) container).bind(false, null);
}
container.backgroundProcess();
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i]);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("containerBase.backgroundProcess.error"), t);
} finally {
if (container instanceof Context) {
((Context) container).unbind(false, originalClassLoader);
}
}
}
}
ContainerBackgroundProcessor 的一个重点是调用 Container 的 backgroundProcess() 方法,然后递归处理调用 ContainerBackgroundProcessor#processChildren 来调用子容器的 backgroundProcess(),另外,如果 Container 是 Context 的实现来的话,还会调用 Context#bind 方法。
StandardEngine 没有重载 ContainerBase 的 backgroundProcess() 方法,而 StandardHost、StandardContext、StandardWrapper 都没有重新给 backgroundProcessorDelay 赋值,所以这些类的 getBackgroundProcessorDelay() 返回的值是 -1,因此都这些类的 backgroundProcess() 都将会执行。
2.5 ContainerBase#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;
Cluster cluster = getClusterInternal();
if (cluster != null) {
try {
cluster.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.cluster",
cluster), 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);
}
可以看出 ContainerBase 的 backgroundProcess() 方法依次调用 Cluster、Realm、Valve 的 backgroundProcess() 方法,然后触发一个 Lifecycle.PERIODIC_EVENT 事件。
Cluster、Realm、Valve 不是本文的重点,这里就不细讲了。
小结
本文介绍了 Engine 的 initInternal 和 startInternal 方法。在这两个方法里 StandardEngine 主要做的事情就是调用父类 ContainerBase 的重载方法,在 ContainerBase 的 startInternal 方法里,依次调用了 Container 里有的 Cluster 对象、Realm对象、子Container对象、Pipeline对象的 start 方法,并且异步调用了 Container 自身的 backgroundProcess 方法,以及递归调用了子类的 backgroundProcess 方法。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。