热部署和热加载类似,都是在不重启Tomcat情况下,使最新代码生效。
热部署与热加载的区别:
热部署表示重新部署应用,执行容器结构是Host,表示主机。
热加载表示重新加载类或jar包,执行容器结构是Context,表示应用。
执行时机
engine.start()
org.apache.catalina.core.ContainerBase#startInternal
开启一个定时任务执行类ContainerBackgroundProcessorMonitor
每60s执行一次
image.png
org.apache.tomcat.util.threads.ScheduledThreadPoolExecutor#scheduleWithFixedDelay
command参数是ContainerBackgroundProcessorMonitor任务类。ContainerBackgroundProcessor任务类,是第一次ContainerBackgroundProcessorMonitor任务类启动后延迟10s后每10s执行一次。
image.png
org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessorMonitor
image.png
threadStart方法是60s执行一次
org.apache.catalina.core.ContainerBase#threadStart
开启后台线程定期检查会话超时。backgroundProcessorDelay=10。会在utilityExecutorWrapper线程池执行ContainerBackgroundProcessor任务类
image.png
后台方法backgroundProcess
org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor
在一个固定的延迟内,执行容器及其孩子的后台方法backgroundProcess
image.png
1、首先执行StandardEngine的backgroundProcess方法
org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor#processChildren
container = StandardEngine的backgroundProcess方法
image.png
org.apache.catalina.core.ContainerBase#backgroundProcess
对于这个方法是所有容器的后台任务,这些任务都需要执行:
①、集群服务器心跳Cluster
如果一个容器拥有自己的类加载器,那么查看是否需要进行热加载
检查Session是否过期
image.png
②、执行每个容器对于的Realm对应的后台任务
image.png
③、执行每个容器中pipeline中的每个valve的后台任务和发布PERIODIC_EVENT事件
其中热部署就是在发布事件中
image.png
org.apache.catalina.util.LifecycleBase#fireLifecycleEvent
image.png
容器是StandardEngine时,lifecycleListeners包含MapperListener、EngineConfig这里什么都不做。
2、StandardHost的processChildren方法
当前容器的孩子容器是StandardHost,再次调用processChildren方法(是个递归方法)
image.png
StandardHost.backgroundProcess方法
image.png
直接看org.apache.catalina.util.LifecycleBase#fireLifecycleEvent方法
image.png
此时lifecycleListeners有MapperListener、ContextConfig$HostWebXmlCacheCleaner、HostConfig
热部署
直接查看lifecycleListeners中HostConfig的方法
org.apache.catalina.startup.HostConfig#lifecycleEvent
image.png
org.apache.catalina.startup.HostConfig#check()
image.png
image.png
checkResources检查资源:以进行重新部署和重新加载、对旧版本的进行检查卸载、最后进行热部署应用
checkResources
org.apache.catalina.startup.HostConfig#checkResources
image.png
检查资源文件是否被修改
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;
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) {
// 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)) {
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);
}
reload(app, docBaseFile, resource.getAbsolutePath());
} else {
reload(app, null, null);
}
// 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;
}
// 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();
// 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();
}
}
deployApps部署应用方式
org.apache.catalina.startup.HostConfig#deployApps()
image.png
1、./webapps目录下部署WARs包、文件夹
2、./conf/Catalina/localhost文件下部署
热加载
在调用StandardHost容器的backgroundProcess方法
此时有7个孩子容器,这7个孩子都是在webapps目录下部署的项目,除了testservlet都是自带的。这里以自定义的testservlet测试
image.png
reloadable配置
这里还不能直接测试,需要在servel.xml添加了热加载reloadable启动配置<Context path="/testservlet" docBase="testservlet" debug="0" reloadable="true"/> reloadable默认是false
image.png
直接走到testservlet的backgroundProcess方法
image.png
org.apache.catalina.core.StandardContext#backgroundProcess
image.png
org.apache.catalina.loader.WebappLoader#backgroundProcess
reloadable=true才会继续调用modified()方法,查看是否修改了
image.png
org.apache.catalina.loader.WebappLoader#modified
image.png
modified
org.apache.catalina.loader.WebappClassLoaderBase#modified
有一个或多个classes或这resources改变将会被重新加载
1、resourceEntries文件是否被修改
image.png
image.png
resourceEntries是查看WEB-INF/classes目录下
image.png
2、jars是检查/WEB-INF/lib目录下是否改变
①、jar包添加
image.png
②、jar包修改
image.png
③、jar包被移除
image.png
所以,classes下的文件被修改,lib目录下添加、修改或移除jar包将会返回true
总结:
Tomcat的热部署和热加载的概念及区别,执行主体分别是什么。
热部署是对应于应用级别
热加载是重新加载改变的类文件或jar包的变化。
网友评论