Tomcat 系列篇十一-介绍下 Tomcat 里的后台处理和热加载

这部分其实之前在讲线程池的时候也有点带到了, 主要是在这个类里
org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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.backgroundProcess();
// 然后寻找 children
Container[] children = container.findChildren();
for (Container child : children) {
// 如果 backgroundProcessorDelay <= 0 就调用执行
// 否则代表这个 Container 有之前第八篇说的 StartChild 这种
if (child.getBackgroundProcessorDelay() <= 0) {
processChildren(child);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("containerBase.backgroundProcess.error"), t);
} finally {
if (container instanceof Context) {
((Context) container).unbind(false, originalClassLoader);
}
}
}
}

这个触发方式是在 ContainerBase 里的

1
2
3
4
5
6
7
8
protected class ContainerBackgroundProcessorMonitor implements Runnable {
@Override
public void run() {
if (getState().isAvailable()) {
threadStart();
}
}
}

而在这个 threadStart 里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}
}

就调用了线程池的 scheduleWithFixedDelay 方法提交了这个 ContainerBackgroundProcessor
仔细看代码会发现,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;

}

这个就不用开启后台热加载,而主要的热加载同学应该是
org.apache.catalina.core.StandardContext#backgroundProcess

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public void backgroundProcess() {

if (!getState().isAvailable()) {
return;
}

Loader loader = getLoader();
if (loader != null) {
try {
// 这里就用了 loader 的 backgroundProcess
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.loader", loader), e);
}
}
Manager manager = getManager();
if (manager != null) {
try {
manager.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.manager", manager),
e);
}
}
WebResourceRoot resources = getResources();
if (resources != null) {
try {
resources.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.resources",
resources), e);
}
}
InstanceManager instanceManager = getInstanceManager();
if (instanceManager != null) {
try {
instanceManager.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString(
"standardContext.backgroundProcess.instanceManager",
resources), e);
}
}
super.backgroundProcess();
}

loader 的后台处理就是

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void backgroundProcess() {
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (context != null) {
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
}
}

然后又会回到 context 的 reload,也就是 StandardContext 的 reload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Override
public synchronized void reload() {

// Validate our current component state
if (!getState().isAvailable()) {
throw new IllegalStateException
(sm.getString("standardContext.notStarted", getName()));
}

if(log.isInfoEnabled()) {
log.info(sm.getString("standardContext.reloadingStarted",
getName()));
}

// Stop accepting requests temporarily.
setPaused(true);

try {
stop();
} catch (LifecycleException e) {
log.error(
sm.getString("standardContext.stoppingContext", getName()), e);
}

try {
start();
} catch (LifecycleException e) {
log.error(
sm.getString("standardContext.startingContext", getName()), e);
}

setPaused(false);

if(log.isInfoEnabled()) {
log.info(sm.getString("standardContext.reloadingCompleted",
getName()));
}

}

这样就是线程池结合后台处理,还是有些复杂的。