天天看點

tomcat自動重新加載應用

前言

當應用配置檔案發生變化時,無需重新開機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。

tomcat自動重新加載應用

詳見: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