Tomcat 系列篇十一-介绍下 Tomcat 里的后台处理和热加载
这部分其实之前在讲线程池的时候也有点带到了, 主要是在这个类里org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor1
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
43protected class ContainerBackgroundProcessor implements Runnable {
        
        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
8protected class ContainerBackgroundProcessorMonitor implements Runnable {
        
        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
18protected 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
14public 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#backgroundProcess1
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
48public 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
    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 的 reload1
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
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()));
    }
}
这样就是线程池结合后台处理,还是有些复杂的。