前言
當應用配置檔案發生變化時,無需重新開機tomcat,可以使tomcat重新加載應用。
場景
假設存在一個J2EE應用A,對應war檔案名稱為A.war,部署在tomcat的webapps目錄下,即:CATALINA_HOME/webapps/A.war。
tomcat啟動之後會将A.war解壓,并在$CATALINA_HOME/webapps/目錄下對應生成目錄A,即:CATALINA_HOME/webapps/A。
此時,需要修改A下存在一個配置檔案a_dao.xml,其中配置了一些參數,如:資料庫配置。
為了使應用能夠重新使用最新的配置屬性,有2種方式:
其一:修改配置檔案,直接重新開機tomcat。
其二:修改配置檔案,執行指令:
touch TOMCAT_HOME/webapps/A/WEB-INF/web.xml
,讓tomcat重新加載應用A。
原理
當然,修改配置之後重新開機tomcat這個方式不難了解,應用重新被部署,肯定會使用到最新的配置。
那麼,對于不需要重新開機tomcat,而是讓tomcat重新加載應用,低層的實作原理是什麼呢?
首先,我們來看通過tomcat重新加載應用,我們做了什麼?
我們隻做了一件事情:
touch TOMCAT_HOME/webapps/A/WEB-INF/web.xml
。
顯然,這裡涉及了2個元件:touch指令和tomcat。
OK,下面我們分别解讀這2個元件都做了什麼事情,撥雲見霧地看看這裡面都發生了什麼。
1.touch指令做了什麼
執行了
touch
指令,這個指令的作用是修改檔案的時間戳。
TOUCH(1) User Commands TOUCH(1)
NAME
touch - change file timestamps
SYNOPSIS
touch [OPTION]... FILE...
DESCRIPTION
Update the access and modification times of each FILE to the current time.
A FILE argument that does not exist is created empty, unless -c or -h is supplied.
複制
什麼意思?在實踐中發現,touch指令會将檔案的“建立時間”,“通路時間”和“修改時間”這三個時間戳都修改為目前時間。
這意味着什麼呢?tomcat會認為這個檔案發生了變化!
那麼,是不是可以了解為一旦tomcat監測到應用的描述檔案web.xml發生變化之後就會主動重新加載應用呢?
如下是一個實際的tomcat重新加載應用的輸出日志。
九月 15, 2017 8:21:49 下午 org.apache.catalina.startup.HostConfig reload
資訊: Reloading context [/test-javaweb]
九月 15, 2017 8:21:49 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/test-javaweb] has started
九月 15, 2017 8:21:49 下午 org.apache.catalina.ha.session.DeltaManager stopInternal
資訊: Manager [localhost#/test-javaweb] expiring sessions upon shutdown
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/core_rt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/core is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jsp/jstl/core is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/fmt_rt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/fmt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jsp/jstl/fmt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jsp/jstl/functions is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://jakarta.apache.org/taglibs/standard/permittedTaglibs is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://jakarta.apache.org/taglibs/standard/scriptfree is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/sql_rt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/sql is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jsp/jstl/sql is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/xml_rt is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jstl/xml is already defined
九月 15, 2017 8:21:50 下午 org.apache.catalina.startup.TaglibUriRule body
資訊: TLD skipped. URI: http://java.sun.com/jsp/jstl/xml is already defined
九月 15, 2017 8:21:50 下午 org.chench.test.web.listener.TestWebListener contextInitialized
資訊: TestWebListener contextInitialized
九月 15, 2017 8:21:50 下午 org.chench.test.web.filter.TestWebFilter init
資訊: TestWebFilter context path=/test-javaweb
九月 15, 2017 8:21:50 下午 org.apache.catalina.core.StandardContext reload
資訊: Reloading Context with name [/test-javaweb] is completed
複制
經過實驗發現,一旦web應用的web.xml檔案的時間戳發生變化(建立時間,修改時間或通路時間發生變化),tomcat就會重新加載應用。
2.tomcat如何知道應用的web.xml發生了變化
在上述tomcat日志中存在如下資訊:
九月 15, 2017 8:21:49 下午 org.apache.catalina.startup.HostConfig reload
資訊: Reloading context [/test-javaweb]
複制
追蹤tomcat源碼發現,這個日志資訊是在org.apache.catalina.startup.HostConfig類的reload方法中列印的:
/*
* Note: If either of fileToRemove and newDocBase are null, both will be
* ignored.
*/
private void reload(DeployedApplication app, File fileToRemove, String newDocBase) {
if(log.isInfoEnabled())
log.info(sm.getString("hostConfig.reload", app.name));
...
}
複制
繼續追蹤tomcat源碼發現,在org.apache.catalina.core.ContainerBase中存在如下實作:
/**
* Start the background thread that will periodically check for
* session timeouts.
*/
protected void threadStart() {
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();
}
複制
也就是說,Tomcat在啟動時會啟動一個背景線程ContainerBackgroundProcessor,用于監控web應用的配置變化。
預設情況下,該線程會每隔10秒檢查一次web應用的web.xml檔案的變化。
該值在server.xml中Engine元素上配置屬性:backgroundProcessorDelay。

詳見:http://tomcat.apache.org/tomcat-7.0-doc/config/engine.html。
實際上,org.apache.catalina.startup.HostConfig中存在如下方法:
addWatchedResources(DeployedApplication app, String docBase, Context context)
複制
這個方法是tomcat在部署應用的時候調用的,它将應用的web.xml檔案添加為監控對象。
背景線程通過檢測該檔案的時間戳是否發生變化,進而确定是否需要重新加載應用。
另外,需要特别注意:該特性需要配置Host元素的autoDeploy屬性為true;若為false,即使應用web.xml檔案發生變化,tomcat也不會重新加載應用。
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
複制
因為org.apache.catalina.startup.HostConfig在check時會判斷autoDeploy是否為true。
/**
* Check status of all webapps.
*/
protected void check() {
if (host.getAutoDeploy()) {
// Check for resources modification to trigger redeployment
DeployedApplication[] apps =
deployed.values().toArray(new DeployedApplication[0]);
for (int i = 0; i < apps.length; i++) {
if (!isServiced(apps[i].name))
checkResources(apps[i], false);
}
// Check for old versions of applications that can now be undeployed
if (host.getUndeployOldVersions()) {
checkUndeploy();
}
// Hotdeploy applications
deployApps();
}
}
複制
【參考】
https://www.mulesoft.com/cn/tcat/tomcat-reload