> Linux服务器 > Tomcat >

Tomcat学习之Host

引言

tomcat中一个Host代表一个虚拟主机,一个虚拟主机上可以有多个应用。Host的默认实现是StandardHost,它的pipeline有两个阀门:ErrorReportValve和StandardHostValve。前者负责选择context来处理用户请求,后者负责处理错误信息。本文主要讲解host容器部署web应用的过程以及它的常用配置项。

部署

所有与部署有关的方法都在类HostConfig中,包括目录的创建,资源的检查、应用部署等操作。其中应用部署是在deployApps中完成的

  1. protected void deployApps() {  
  2.   
  3.     File appBase = appBase();  
  4.     File configBase = configBase();  
  5.     String[] filteredAppPaths = filterAppPaths(appBase.list());  
  6.     // Deploy XML descriptors from configBase  
  7.     deployDescriptors(configBase, configBase.list());  
  8.     // Deploy WARs  
  9.     deployWARs(appBase, filteredAppPaths);  
  10.     // Deploy expanded folders  
  11.     deployDirectories(appBase, filteredAppPaths);  
  12.       
  13. }  
Tomcat应用部署由三部分组成:1、部署描述符;2、部署WAR包;3、部署目录

部署描述符

首先获取${TOMCAT_HOME}\conf\Catalina\localhos目录下的所有xml文件,然后调用deployDescriptor方法一个一个部署,来看看deployDescriptor方法的实现
[html] view plaincopyprint?
  1. protected void deployDescriptor(ContextName cn, File contextXml) {  
  2.     ...  
  3.     DeployedApplication deployedApp = new DeployedApplication(cn.getName());  
  4.     ...  
  5.     deployedApp.redeployResources.put(contextXml.getAbsolutePath(),Long.valueOf(contextXml.lastModified()));  
  6.     deployedApp.redeployResources.put(docBase.getAbsolutePath(),Long.valueOf(docBase.lastModified()));  
  7.     deployedApp.reloadResources.put(resource.getAbsolutePath(), Long.valueOf(resource.lastModified()));  
  8.          
  9.     // Add the global redeploy resources (which are never deleted) at  
  10.     // the end so they don't interfere with the deletion process  
  11.     addGlobalRedeployResources(deployedApp);  
  12.     ...  
  13.     deployed.put(context.getName(), deployedApp);  
  14. }  
其实就做了几件事:
1、创建Context,因为一个文件就代表一个context,这里并没有展示有关context的代码
2、封装deployedApp,将代表context的xml文件路径和docBase指向的目录加入redeployResources中,将web.xml文件加入reloadResources中
3、最后将应用名与应用加入deployed这个map文件中,这个map代表被部署的应用。

部署WAR包

首先获取${TOMCAT_HOME}\webapps目录下的所有war包,然后调用deployWAR方法一个一个部署,具体做了以下几件事:
1、如果${appname}/META-INF/context.xml文件存在,利用digester将其解析为一个context,如果不存在,则创建org.apache.catalina.core.StandardContext实例;
2、为这个context设置一些属性,比如name,path,DocBase等;
3、然后做了部署描述符的后两步,封装deployedApp,并将其加入map中
第一步把context加入host时会做解包操作,具体的解包操作是在类ExpandWar的expand方法中完成的
[html] view plaincopyprint?
  1.  while (jarEntries.hasMoreElements()) {  
  2.     JarEntry jarEntry = jarEntries.nextElement();  
  3.     String name = jarEntry.getName();  
  4.     File expandedFile = new File(docBase, name);  
  5.   
  6.     int last = name.lastIndexOf('/');  
  7.     if (last >= 0) {  
  8.         File parent = new File(docBase, name.substring(0, last));  
  9.         if (!parent.mkdirs() && !parent.isDirectory()) {  
  10.             throw new IOException(  
  11.                     sm.getString("expandWar.createFailed", parent));  
  12.         }  
  13.     }  
  14.     if (name.endsWith("/")) {  
  15.         continue;  
  16.     }  
  17.     expand(input, expandedFile);  
  18. }         
对于'/'结尾的是目录直接创建目录,不是以‘/’结尾的先创建空文件,然后调用expand方法向文件中写数据
[html] view plaincopyprint?
  1. private static void expand(InputStream input, File file)  
  2.     throws IOException {  
  3.     BufferedOutputStream output = null;  
  4.     try {  
  5.         output =   
  6.             new BufferedOutputStream(new FileOutputStream(file));  
  7.         byte buffer[] = new byte[2048];  
  8.         while (true) {  
  9.             int n = input.read(buffer);  
  10.             if (n <= 0)  
  11.                 break;  
  12.             output.write(buffer, 0, n);  
  13.         }  
  14.     } finally {  
  15.         if (output != null) {  
  16.             try {  
  17.                 output.close();  
  18.             } catch (IOException e) {  
  19.                 // Ignore  
  20.             }  
  21.         }  
  22.     }  
  23. }  
部署目录和部署war文件类似,只是不需要解包操作。

配置

Host元素中有以下属性,下面分别讲解:
appBase:虚拟主机下应用的基准目录,可以是一个绝对路径,也可以是一个相对路径(相对于${CATALINA_BASE}),如果没有配置,默认为webapps.
xmlBase:部署描述文件的基准目录,可以是一个绝对路径,也可以是一个相对路径(相对于${CATALINA_BASE}),默认为${TOMCAT_HOME}\server\localhost
createDirs:设置为true,会在tomcat启动时创建appBase和xmlBase所指定的目录,这个值默认为true.如果这个值为true,且在创建目录的过程中报错会在控制台上打印错误信息,但不会导致应用挂掉
autoDeploy:默认为true,会定期地检查appBase和xmlBase中的文件变化,会导致一个web应用重新加载
deployOnStartup:默认为true,指示web应用启动时会自动部署xmlBase目录下的应用
Host的标准实现类StandardHost还支持以下属性:
copyXML:默认为false,如果为true,/META-INF/context.xml文件将会被拷贝到xmlBase中,xml文件名为应用名.如果这样做,tomcat会一直用xmlBase中的xml文件,即使/META-INF/context.xml文件更新。
deployXML:默认为true,会解析/META-INF/context.xml文件。如果为false会忽略/META-INF/context.xml文件
unpackWARs:默认为true,appBase目录下的war包会被解包,appBase外的war包不会解包
如果使用的是标准的HOST实现,在TOMCAT启动的时候(deployOnStartup=true)以下行为会自动触发:
1、xmlBase目录(默认为$CATALINA_BASE/conf/[engine_name]/[host_name])下面的xml文件代表一个上下文描述符,每个xml文件代表一个web应用,这些web应用会优先部署。Context元素的docBase属性为对应war包的名字。
2、appBase中的其他war包,由于没有对应的context.xml没有被优先部署,并不意味着这些war不会被部署,只能说不会被优先部署。war包的上下文路径为‘/war包的名字’(没有后缀),这里有个意外,ROOT.war对应的上下文路径为‘/’,多级context可以用#来定义,如foo#bar.war的上下文路径为‘/foo/bar’。
如果unpackWARs属性设置为true,在tomcat启动的时候会解压war包。注意:如果在tomcat停止时你修改了war包,确保删除解压出来的目录然后再重启tomcat,以便在重启时新修改的war包会被解压。如果copyXML属性为true(默认为false),那么会检查每个war包下/META-INF/context.xml文件是否存在,如果存在会将其拷贝到xmlBase中并重命名为war包的名字。
3、appBase中的其他子目录,由于没有对应的context.xml也没有被优先部署,并不意味着这些war不会被部署。这些目录的上下文路径为‘/目录名’,如果目录名为ROOT,上下文路径为‘/’,foo#bar目录的上下文路径为‘/foo/bar’。如果copyXML属性为true(默认为false),那么会检查每个目录下/META-INF/context.xml文件是否存在,如果存在会将其拷贝到xmkBase中并重命名为war的名字。
 
(责任编辑:IT)