Tomcat源码分析 之 Tomcat如何监测部署应用的改变并进行重新部署
来源:互联网 发布:盛势网络剧最新消息 编辑:程序博客网 时间:2024/06/05 03:21
使用eclipse和tomcat做web应用开发的童鞋都知道,有一个很强大的功能就是--我们的文件改变的时候Tomcat会自动的重新部署应用,给我们的开发调试带来了很大的便利,但是它是怎么实现的呢?我们下面来关注一下原理和Tomcat的处理办法。
我们都知道,我们修改了文件,并且保存了它,这个文件的修改时间就会被改变,当然了我们的应用处理逻辑也可能发生了变化,这时候我们就应该重新发布一下应用,让它切换到最新的逻辑上面。那么我们现在我们可以归结出Tomcat需要做两件事:
1.检测部署的应用文件是否被修改。
2.如果修改了那么需要重新部署应用。
关于如何检测文件状态的变化可以参考《使用commons-vfs监听文件系统》 使用了里面的第二种方法。可以看Tomcat的源代码,这里使用的是8.X:
这里的检测是在org.apache.catalina.startup.HostConfig中完成的:
首先添加要观察的文件:
/** * Add watched resources to the specified Context. * @param app HostConfig deployed app * @param docBase web app docBase * @param context web application context */ protected void addWatchedResources(DeployedApplication app, String docBase, Context context) { // FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, // WEB-INF/*.xml), where we would only check if at least one // resource is newer than app.timestamp File docBaseFile = null; if (docBase != null) { docBaseFile = new File(docBase); if (!docBaseFile.isAbsolute()) { docBaseFile = new File(host.getAppBaseFile(), docBase); } } 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())); } }
其次是检测文件时间戳:
/** * Check resources for redeployment and reloading. */ protected synchronized void checkResources(DeployedApplication app) { String[] resources = app.redeployResources.keySet().toArray(new String[0]); for (int i = 0; i < resources.length; i++) { File resource = new File(resources[i]); if (log.isDebugEnabled()) log.debug("Checking context[" + app.name + "] redeploy resource " + resource); long lastModified = app.redeployResources.get(resources[i]).longValue(); if (resource.exists() || lastModified == 0) { if (resource.lastModified() > lastModified) { if (resource.isDirectory()) { // No action required for modified directory app.redeployResources.put(resources[i], Long.valueOf(resource.lastModified())); } else if (app.hasDescriptor && resource.getName().toLowerCase( Locale.ENGLISH).endsWith(".war")) { // Modified WAR triggers a reload if there is an XML // file present // The only resource that should be deleted is the // expanded WAR (if any) Context context = (Context) host.findChild(app.name); String docBase = context.getDocBase(); if (!docBase.toLowerCase(Locale.ENGLISH).endsWith(".war")) { // This is an expanded directory File docBaseFile = new File(docBase); if (!docBaseFile.isAbsolute()) { docBaseFile = new File(host.getAppBaseFile(), docBase); } ExpandWar.delete(docBaseFile); // Reset the docBase to trigger re-expansion of the // WAR context.setDocBase(resource.getAbsolutePath()); } reload(app); // Update times app.redeployResources.put(resources[i], Long.valueOf(resource.lastModified())); app.timestamp = System.currentTimeMillis(); boolean unpackWAR = unpackWARs; if (unpackWAR && context instanceof StandardContext) { unpackWAR = ((StandardContext) context).getUnpackWAR(); } if (unpackWAR) { addWatchedResources(app, context.getDocBase(), context); } else { addWatchedResources(app, null, context); } return; } else { // Everything else triggers a redeploy // (just need to undeploy here, deploy will follow) undeploy(app); deleteRedeployResources(app, resources, i, false); return; } } } else { // There is a chance the the resource was only missing // temporarily eg renamed during a text editor save try { Thread.sleep(500); } catch (InterruptedException e1) { // Ignore } // Recheck the resource to see if it was really deleted if (resource.exists()) { continue; } if (lastModified == 0L) { continue; } // Undeploy application undeploy(app); deleteRedeployResources(app, resources, i, true); return; } } 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(); if (resource.lastModified() != lastModified || update) { if (!update) { // Reload application reload(app); 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(); } }
可以看到它使用的是File的lastModified()方法。
最后这个检测过程是什么时候触发呢?我们知道配置
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
Host的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]); } // Check for old versions of applications that can now be undeployed if (host.getUndeployOldVersions()) { checkUndeploy(); } // Hotdeploy applications deployApps(); } }
我现在告诉你这个check()是周期性执行的话,你肯定会问如何周期性执行,接着看:
可以看到一个周期性事件触发了这个过程。那么谁会周期性的去发送这个周期性事件呢?
org.apache.catalina.core.StandardEngine 就是我们Engine标签的实际代表者,这个类继承了org.apache.catalina.core.ContainerBase,其中有一段:
在容器启动的时候会创建一个守护线程去周期性的发出周期性事件
那么肯定还有最后一个问题,这个周期性事件的间隔是多少,怎么设置?
默认是10秒 如果我们想改变这个值怎么办?
打开你的server.xml 然后在Engine标签中添加属性
<Engine name="Catalina" defaultHost="localhost" backgroundProcessorDelay="20">然后我们改动一下代码:
然后我们可以看到改变了周期性事件的间隔。
目前这个周期性事件是从engine触发的,如果我们想让其他容器触发怎么办?
对应的标签添加属性backgroundProcessorDelay。只要继承了org.apache.catalina.core.ContainerBase这个容器的对应标签都可以出发出周期性事件。
- Tomcat源码分析 之 Tomcat如何监测部署应用的改变并进行重新部署
- tomcat 进行远程重新部署工程项目
- 如何在tomcat部署应用
- tomcat的部署及应用
- Tomcat源码分析(十)--部署器
- Tomcat源码分析(十)--部署器
- 部署并启动Tomcat
- Tomcat重新部署无作用`
- tomcat重新部署,图片消失。
- shell脚本--部署应用到tomcat并启动tomcat
- Tomcat 如何部署多个应用
- linux中如何部署tomcat应用
- tomcat部署web应用
- Tomcat部署WEB应用
- Tomcat 部署Web应用
- Tomcat应用部署
- Tomcat部署web应用
- tomcat部署web应用
- mac版 Intellij IDEA 弹窗报 this license XXXXXXXX has been cancelled
- 对于内存泄露的查找
- QPainterPath 用法
- [LeetCode]401. Binary Watch
- Error
- Tomcat源码分析 之 Tomcat如何监测部署应用的改变并进行重新部署
- Makefile 中:= ?= += =的区别
- 用Swift3实现n*n阶矩阵逆时针输出
- tensorflow 下的滑动平均模型 —— tf.train.ExponentialMovingAverage
- Maven集成至Myeclipse
- ubuntu tomcat自启动
- 算法/调度算法/ShortJobFirst(SJF)短作业优先调度算法
- BZOJ 2209 [Jsoi2011] 括号序列
- 【POJ3683】Priest John's Busiest Day(Special Judge)