Tomcat应用自动重部署与WatchedResources

上篇文章介绍了Tomcat中Context的描述文件使用方式。这次我们介绍下Context描述文件中可以配置的并且是默认就已经包含的一个配置,通过它,我们的应用在每次文件更新后才会重新加载。

这个配置就是Context中的嵌套配置WatchedResource。

官方文档这样描述:

The auto deployer will monitor the specified static resource of the web application for updates, and will reload the web application if it is updated. The content of this element must be a string.

这里配置的静态资源会被自动部署器监控用于更新和重新加载应用。

那为什么说这个配置是默认的呢?

因为在CATALINA_HOME/conf/ 目录下,也包含一个名为context.xml,用于做为每个应用的默认配置来加载,内容如下:

看过上一篇文章(Tomcat目录部署与Context描述文件context.xml)的朋友,应该还记得这个Context的描述文件,是在Context进行初始化时,在ContextConfig中,通过Digester解析并设置的。而这个默认的配置,是在自定义的描述文件之前加载的。

if (defaultContextFile.exists()) {
    try {
        URL defaultContextUrl = defaultContextFile.toURI().toURL();
        processContextConfig(digester, defaultContextUrl);//这里加载
    } catch (MalformedURLException e) {
        log.error(sm.getString(
                "contextConfig.badUrl", defaultContextFile), e);
    }
}

此时,StandardContext中的watchedResources数组,会被设置

public void addWatchedResource(String name) {

    synchronized (watchedResourcesLock) {
        String results[] = new String[watchedResources.length + 1];
        for (int i = 0; i < watchedResources.length; i++)
            results[i] = watchedResources[i];
        results[watchedResources.length] = name;
        watchedResources = results;
    }
    fireContainerEvent("addWatchedResource", name);
}

当然,这里的属性仅仅是赋值给Context,方便后面用的时候提取。

在部署的流程中,我们当进并没有提关于重部署的事情,其实在部署的时候,会设置一些内容,用于后面重部署的监控。例如:

/**
 * Any modification of the specified (static) resources will cause a
 * redeployment of the application. If any of the specified resources is
 * removed, the application will be undeployed. Typically, this will
 * contain resources like the context.xml file, a compressed WAR path.
 * The value is the last modification time.
 */
public final LinkedHashMap<String, Long> redeployResources =
        new LinkedHashMap<>();
deployedApp.redeployResources.put(
        contextXml.getAbsolutePath(),
        Long.valueOf(contextXml.lastModified()));
deployedApp.redeployResources.put(docBase.getAbsolutePath(),
        Long.valueOf(docBase.lastModified()));

这个Map用来记录应用中的一些描述文件等信息,如果这些内容被个性时会重新加载,如果被删除,则会触发应用的卸载。

而我们前面的watchedResources,对应的是另一个Map,这个Map在添加完redeployResources之后,会经过addWatchedResources设置

/**
 * Any modification of the specified (static) resources will cause a
 * reload of the application. This will typically contain resources
 * such as the web.xml of a webapp, but can be configured to contain
 * additional descriptors.
 * The value is the last modification time.
 */
public final HashMap<String, Long> reloadResources = new HashMap<>();
if (unpackWAR) {
    deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
            Long.valueOf(expandedDocBase.lastModified()));
    addWatchedResources(deployedApp,
            expandedDocBase.getAbsolutePath(), context);
} else {
    addWatchedResources(deployedApp, null, context);
}

//这里就会遍历前面设置好的WatchedResources,然后将其添加到DeployedApp中

String[] watchedResources = context.findWatchedResources();
for (int i = 0; i < watchedResources.length; i++) {
    File resource = new File(watchedResources[i]);
    if (!resource.isAbsolute()) {
        if (docBase != null) {
            resource = new File(docBaseFile, watchedResources[i]);
        } else {
            if(log.isDebugEnabled())
                log.debug("Ignoring non-existent WatchedResource '" +
                        resource.getAbsolutePath() + "'");
            continue;
        }
    }
    if(log.isDebugEnabled())
        log.debug("Watching WatchedResource '" +
                resource.getAbsolutePath() + "'");
    app.reloadResources.put(resource.getAbsolutePath(),//设置在这里
            Long.valueOf(resource.lastModified()));
}

真实的监控时,是读取上面这两个Map。我们很早之前的文章里讲过,Tomcat的每级容器背后,都运行着一个后台线程,在轮询着,招待一个backupProcess。像过期Session的清理等都是通过这些来操作的。(对于过期的session,Tomcat做了什么?

这次的重部署当然也不例外。后台线程会不断检查这两个Map中指定的应用的资源。如果资源的更新时间发生变化,会触发应用的重部署,如果其中的redeploy资源发生删除,应用就会被卸载。

首先是检查redeployResources

/**
 * Check resources for redeployment and reloading.
 *
 * @param app   The web application to check
 * @param skipFileModificationResolutionCheck
 *              When checking files for modification should the check that
 *              requires that any file modification must have occurred at
 *              least as long ago as the resolution of the file time stamp
 *              be skipped
 */
protected synchronized void checkResources(DeployedApplication app,
        boolean skipFileModificationResolutionCheck) {
    String[] resources =
        app.redeployResources.keySet().toArray(new String[0]);
    // Offset the current time by the resolution of File.lastModified()
    long currentTimeWithResolutionOffset =
            System.currentTimeMillis() - FILE_MODIFICATION_RESOLUTION_MS;

...

// 然后是遍历reloadResources

resources = app.reloadResources.keySet().toArray(new String[0]);
boolean update = false;
for (int i = 0; i < resources.length; i++) {
    File resource = new File(resources[i]);
    if (log.isDebugEnabled()) {
        log.debug("Checking context[" + app.name + "] reload resource " + resource);
    }
    long lastModified = app.reloadResources.get(resources[i]).longValue();
    // File.lastModified() has a resolution of 1s (1000ms). The last
    // modified time has to be more than 1000ms ago to ensure that
    // modifications that take place in the same second are not
    // missed. See Bug 57765.
    if ((resource.lastModified() != lastModified &&
            (!host.getAutoDeploy() ||
                    resource.lastModified() < currentTimeWithResolutionOffset ||
                    skipFileModificationResolutionCheck)) ||
            update) {
        if (!update) {
            // Reload application
            reload(app, null, null);
            update = true;
        }
        // Update times. More than one file may have been updated. We
        // don't want to trigger a series of reloads.
        app.reloadResources.put(resources[i],
                Long.valueOf(resource.lastModified()));
    }
    app.timestamp = System.currentTimeMillis();
}

重新部署后,会更新Map中对应的更新时间戳,下一轮检查继续。

不过这里需要注意一点,自动部署这个功能,需要Host的autoDeploy属性开启,否则是不会自动部署的

ps: 对源码,技术感兴趣的朋友,可以加我个人微信一起交流。:

          觉得本文对你有帮助?请分享给更多人

关注『 Tomcat那些事儿  』 ,发现更多精彩文章!了解各种常见问题背后的原理与答案。深入源码,分析细节,内容原创,欢迎关注。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值