当前位置: 代码迷 >> Web前端 >> Tomcat调度web应用
  详细解决方案

Tomcat调度web应用

热度:225   发布时间:2012-12-23 11:28:15.0
Tomcat部署web应用

今天Max对大家培训了一下web程序在tomcat下正确的部署方式。

内容主要包括了:

1.java的classloader在tomcat下的体系结构

http://www.jaxmao.org/tomcat-docs/class-loader-howto.html

写道
象许多服务器程序一样,Tomcat 5安装各种不同类型的类loaders(那就是,实现java.lang.ClassLoader的类)来允许容器的不同部分,以及运行在容器上的网络程序,可以访问不同贮存部分的类和资源。这个机制是用来提供Servlet Specification, 2.4版――特别是9.4 和 9.6章节里定义的功能。

In a J2SE 2 (that is, J2SE 1.2 or later) environment, class loaders are arranged in a parent-child tree. Normally, when a class loader is asked to load a particular class or resource, it delegates the request to a parent class loader first, and then looks in its own repositories only if the parent class loader(s) cannot find the requested class or resource. The model for web application class loaders differs slightly from this, as discussed below, but the main principles are the same.

当Tomcat 5启动后,它产生一组类装载器,这些类装载器被组织成如下的父子关系,父类装载器在子类装载器之上:


Bootstrap
|
System
|
Common
/ \
Catalina Shared
/ \
Webapp1 Webapp2 ...



每一个类装载器的特征,包括可被使用的类和资源的来源,在下面的章节有详细讨论。

Class Loader Definitions
如上面的图表显示,Tomcat 5在它启动时产生如下的类装载器:

Bootstrap - 这个类装载器包含Java虚拟器提供的基本的运行时间类,加上在System Extensions 目录( $JAVA_HOME/jre/lib/ext )里的JAR文件中所有的类。注意――有些Java虚拟器(JVMs)也许把它作为不止一个类装载器用,或者作为一个类装载器用。
System - 这个类装载器通常是以CLASSPATH环境变量的内容为基础来初始化的。所有的这些类既可被Tomcat内部classes使用,又可被网络程序使用。不过,标准的Tomcat 5启动脚本( $CATALINA_HOME/bin/catalina.sh or %CATALINA_HOME%\bin\catalina.bat )完全忽略了CLASSPATH环境变量自身的内容,相反从下面的贮藏室去建造系统类装载器:
$CATALINA_HOME/bin/bootstrap.jar - 包含用来初始化Tomcat 5服务器的main()方法,以及它所依赖的类装载器执行类。
$JAVA_HOME/lib/tools.jar - 包含用来把JSP页面转换成servlet类的"javac"编译器。
$CATALINA_HOME/bin/commons-logging-api.jar - Jakarta commons 记录应用程序界面。
$CATALINA_HOME/bin/commons-daemon.jar - Jakarta commons daemon API.
jmx.jar - JMX 1.2 执行。
Common - This class loader contains additional classes that are made visible to both Tomcat internal classes and to all web applications. Normally, application classes should NOT be placed here. All unpacked classes and resources in $CATALINA_HOME/common/classes, as well as classes and resources in JAR files under the $CATALINA_HOME/commons/endorsed, $CATALINA_HOME/commons/i18n and $CATALINA_HOME/common/lib directories, are made visible through this class loader. By default, that includes the following:
commons-el.jar - Jakarta commons el, 执行Jasper使用的表达语言。
jasper-compiler.jar - The JSP 2.0 编译器。
jasper-compiler-jdt.jar - The Eclipse JDT Java compiler.
jasper-runtime.jar - The JSP 2.0 运行时间。
jsp-api.jar - The JSP 2.0 应用编程界面(API)。
naming-common.jar - 被Tomcat 5用来代表in-memory命名contexts的JNDI实现。
naming-factory.jar - 被Tomcat 5用来决定企业资源索引(EJB, connection pools)的JNDI实现。
naming-factory-dbcp.jar - Jakarta commons DBCP, providing a JDBC connection pool to web applications. The classes have been moved out of their default org.apache.commons package.
naming-java.jar - Handler for the java: namespace.
naming-resources.jar - The specialized JNDI naming context implementation used to represent the static resources of a web application. This is not related to the support of the J2EE ENC, and cannot be removed.
servlet-api.jar - The Servlet 2.4 API.
tomcat-i18n-**.jar - Optional JARs containing resource bundles for other languages. As default bundles are also included in each individual JAR, they can be safely removed if no internationalization of messages is needed.
Catalina - 这个类装载器被初始化后包含执行Tomcat 5自身必须的所有类和资源。这些类和资源完全可被网络程序使用。所有的在$CATALINA_HOME/server/classes里未包装的类和资源,以及$CATALINA_HOME/server/lib下JAR文件里的类和资源,通过这个类装载器可被使用。在默认的情况下,有下面的这些:
catalina.jar - Tomcat 5的执行Catalina servlet容器部分。
catalina-ant.jar - Some Ant tasks which can be used to manage Tomcat using the manager web application.
catalina-optional.jar - Some optional components of Catalina.
commons-modeler.jar - A model MBeans implementation used by Tomcat to expose its internal objects through JMX.
servlets-xxxxx.jar - 与每个内部servlet相关联的类,这些内部servlet提供一部分Tomcat的功能。它们是分离开的,这样一来,如果相应的服务不需要的话,它们就可以完全被删除,或者它们需要特别的安全管理员许可。
tomcat-coyote.jar - Coyote API.
tomcat-http11.jar - 独立的Java HTTP/1.1 连接器。
tomcat-ajp.jar - Classes for the Java portion of the AJP web server connector, which allows Tomcat to run behind web servers such as Apache and iPlanet iAS and iWS.
tomcat-util.jar - 一些Tomcat必需的Utility类。
Shared - 这个类装载器是放置被所有网络程序共享的类和资源的地方(除非Tomcat内部classes也需要访问它们,要是这样,你就得把它们放在Common这个类装载器)。所有的在$CATALINA_BASE/shared/classes 里未包装的类和资源,以及$CATALINA_BASE/shared/lib 下JAR文件里的类和资源,通过这个类装载器可被使用。如果多个Tomcat实例使用同样的$CATALINA_BASE环境变量运行,那么这个类装载器的储藏室就和$CATALINA_BASE有关,而不是和$CATALINA_HOME有关。
WebappX - 为在单个Tomcat 5实例中被部署的每一个网络程序所产生的类装载器。在你的网络程序档案/WEB-INF/classes目录里所有的未包装的类和资源,以及网络程序档案/WEB-INF/lib目录下JAR文件里的类和资源,可被这个Webapp里的程序使用,而不能其他程序使用。
As mentioned above, the web application class loader diverges from the default Java 2 delegation model (in accordance with the recommendations in the Servlet Specification, version 2.3, section 9.7.2 Web Application Classloader). When a request to load a class from the web application's WebappX class loader is processed, this class loader will look in the local repositories first, instead of delegating before looking. There are exceptions. Classes which are part of the JRE base classes cannot be overriden. For some classes (such as the XML parser components in J2SE 1.4+), the J2SE 1.4 endorsed feature can be used (see the common classloader definition above). Last, any JAR containing servlet API classes will be ignored by the classloader. All other class loaders in Tomcat 5 follow the usual delegation pattern.

因此,从一个网络程序的角度来看,类和资源的装载以这样的顺序在下列贮藏室进行查找:

你的JVM的Bootstrap类
系统类装载器类(描述如上)
你的网络程序的/WEB-INF/classes
你的网络程序的/WEB-INF/lib/*.jar
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/i18n/*.jar
$CATALINA_HOME/common/lib/*.jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar

?

?

2.使用JMX,配合tomcat的启动配置,监控tomcat内存使用情况

在tomcat的catalina.bat中加入:

set?JAVA_OPTS = %JAVA_OPTS%?-Dcom.sun.management.jmxremote.port=8890?

???????? -Dcom.sun.management.jmxremote.ssl=false?

???????? -Dcom.sun.management.jmxremote.authenticate=false ?

启动tomcat后,就可以通过jdk的bin目录下的jconsole来监控tomcat的内存使用。

?

//调用Tomcat的JMX服务,如停止、启动web应用 
public static boolean callWebModuleMBeanMethod(String appName,String methodName) throws Exception{ 
  MBeanServer mBeanServer = null; 
if (MBeanServerFactory.findMBeanServer(null).size() > 0) {
   mBeanServer=(MBeanServer)MBeanServerFactory.findMBeanServer( null).get(0); 
} else { 
  throw new Exception("cann't find catalina MBeanServer"); 
} 
Set names = null; 
try { 
  names = mBeanServer.queryNames(new ObjectName( "*:j2eeType=WebModule,name=//localhost/"+appName+",*"), null); } catch (Exception e) { 
  throw new Exception("cann't find "+appName+ " web moudule mbean! can't undeploy web app.\n"+e.getMessage()); 
} 
if(names==null || names.size()==0) { 
  log.debug("can't find "+appName+ " web moudule mbean!"); 
  return false; 
} 
ObjectName oname =null; 
Iterator it = names.iterator(); 
if (it.hasNext()) { 
  oname=(ObjectName) it.next(); 
} 
if(oname==null) 
  return false; 
try { 
  mBeanServer.invoke(oname,methodName,null,null); 
  return true; 
} catch (Exception e) { 
  throw new Exception("can't "+methodName+" "+appName+ " web application!\n"+e.getMessage()); 
} 
} 
public static void main(String[] args){ 
  callWebModuleMBeanMethod("app1","stop"); //停止web应用app1   
  callWebModuleMBeanMethod("app1","start"); //启动web应用app1 
} 
?

?

?

通过对上面的一些学习,了解到tomcat在类加载时候会给每个层级的classloader创建实例,并保存在PermGen中,有时候要对一些应用reload的时候,就会从新创建新的classloader实例并将旧的垃圾回收,如果把所有的lib都放在应用的级别,那么当reload的时候,旧的classloader实例往往会因为

  • jdbc 驱动注册
  • 日志框架
  • 将object放到threadlocal没有释放
  • 启动thread但没有终止掉

或者其他程序在web应用中保留了其引用,造成不能垃圾回收,从而造成PermGen的OOME(号称tomcat7已经解决此问题) 。因此,一种比较好的部署方式就是尽量吧公用的lib都放到tomcat的common目录下的lib里去,这样在reload一个web应用时,这个web应用的classloader的内存大小可能很小,大大降低PermGen的OOME的情况出现几率。

?

注意:log4j.jar不能放到common/lib下,因为log4j中读取的是当前log4j.jar所在路径下的对应的log4j.properties。

下面是log4j用XML配置的例子:http://wiki.apache.org/logging-log4j/Log4jXmlFormat

?

?

?

3.使用JNDI配置数据库资源,实现多项目共享数据库连接

在spring中:

<bean id="dbpool1" class="org.springframework.jndi.JndiObjectFactoryBean">


	<property name="jndiName">


	<value>java:comp/env/jdbc/dbpool1</value>


	</property>


     </bean>

?在web应用的META-INF目录中创建context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/EliteHttpAPS" docBase="EliteHttpAPS"
        debug="5" reloadable="true" crossContext="true">
     <Resource name="jdbc/dbpool1" auth="Container" 
     			factory="com.elite.aps.dbservice.http.APSDataSourceFactory" 
               type="javax.sql.DataSource"
               maxActive="10" maxIdle="10" minIdle="5" initialSize="5" encryptkey="rshMlJiUs+A=" 
               dbtype="oracle" logsessionid="true"
               username="flex" password="letmein" 
               driverClassName="oracle.jdbc.driver.OracleDriver"
               validationQuery="select 1 from dual"
               timeBetweenEvictionRunsMillis="60000"
               testWhileIdle="true"
               url="jdbc:oracle:thin:@127.0.0.1:1521:orcl"/>
</Context>

? 其中crossContext="true" 代表了可以再多应用中使用

?

?

?

4.其他一些在项目中需要注意的tomcat中的配置

tomcat的server.xml中,host标签中有个属性是autoDeploy="true",在项目中使用false会比较好一些。

?

  相关解决方案