热部署
在Tomcat中可以通过Host标签设置热部署,当 autoDeploy为true时,在运行中的Tomcat中丢入一个war包,那么Tomcat不需要重启就可以自动加载该war包。
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true" >
Tomcat的容器中都包含有 backgroundProcessorDelay 属性和 backgroundProcess方法,默认的实现是,在每个容器启动时,当backgroundProcessorDelay大于1时(单位是秒),则会周期性的执行当前容器及所有子容器的backgroundProcess方法。
ContainerBase:
protected synchronized void startInternal() throws LifecycleException {
//...省略
threadStart();
}
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();
}
ContainerBackgroundProcessor:
public void run() {
Throwable t = null;
String unexpectedDeathMessage = sm.getString(
"containerBase.backgroundProcess.unexpectedThreadDeath",
Thread.currentThread().getName());
try {
while (!threadDone) {
try {
Thread.sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
// Ignore
}
if (!threadDone) {
Container parent = (Container) getMappingObject();
ClassLoader cl =
Thread.currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
//note 执行所有子容器的backgroundProcessorDelay方法
processChildren(parent, cl);
}
}
} catch (RuntimeException e) {
t = e;
throw e;
} catch (Error e) {
t = e;
throw e;
} finally {
if (!threadDone) {
log.error(unexpectedDeathMessage, t);
}
}
}
protected void processChildren(Container container, ClassLoader cl) {
try {
if (container.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
//执行当前容器的backgroundProcess
container.backgroundProcess();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
//在执行子容器的backgroundProcess
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
backgroundProcessorDelay的设置在Engine标签中,因为默认实现包含所有子容器,所有在最顶层的容器上配置一个即可。
<Engine name="Catalina" defaultHost="localhost" backgroundProcessorDelay="20">
我们知道war包的管理应该是属于它的上一层容器Host,在Host中并没有重写backgroundProcess方法。
StandardHost:
public void backgroundProcess() {
if (!getState().isAvailable())
return;
if (cluster != null) {
try {
cluster.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.cluster", cluster), e);
}
}
if (loader != null) {
try {
loader.backgroundProcess();
} catch (Exception e) {
log.warn(sm.getString("containerBase.backgroundProcess.loader", loader), e);