首页 > 代码库 > Tomcat

Tomcat

1. Tomcat 总体结构

技术分享

从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和 Container。Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。多个 Connector 和一个 Container 就形成了一个 Service,Service 的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。目前咱们公司用的tomcat的版本Server version:Apache Tomcat/7.0.52.

2. Tomcat相关接口

2.1 Tomcat接口视图

技术分享

Catalina:与开始/关闭shell脚本交互的主类,因此如果要研究启动和关闭的过程,就从这个类开始看起。

Server:是整个Tomcat组件的容器,包含一个或多个Service。

Service:Service是包含Connector和Container的集合,Service用适当的Connector接收用户的请求,再发给相应的Container来处理。

Connector:实现某一协议的连接器,如默认的有实现HTTP、HTTPS、AJP协议的。

Container:可以理解为处理某类型请求的容器,处理的方式一般为把处理请求的处理器包装为Valve对象,并按一定顺序放入类型为Pipeline的管道里。Container有多种子类型:Engine、Host、Context和Wrapper,这几种子类型Container依次包含,处理不同粒度的请求。另外Container里包含一些基础服务,如Loader、Manager和Realm。

Engine:Engine包含Host和Context,接到请求后仍给相应的Host在相应的Context里处理。

Host:就是我们所理解的虚拟主机。

Context:就是我们所部属的具体Web应用的上下文,每个请求都在是相应的上下文里处理的。

Wrapper:Wrapper是针对每个Servlet的Container,每个Servlet都有相应的Wrapper来管理。

可以看出Server、Service、Connector、Container、Engine、Host、Context和Wrapper这些核心组件的作用范围是逐层递减,并逐层包含。

下面就是些被Container所用的基础组件:

Loader:是被Container用来载入各种所需的Class。

Manager:是被Container用来管理Session池。

Realm:是用来处理安全里授权与认证。

2.2 Service 和 Server接口重要方法

 

Service接口
 1 public abstract interface Service extends Lifecycle
 2 {
 3 public abstract Container getContainer();
 4 public abstract void setContainer(Container paramContainer);
 5 public abstract String getInfo();
 6 public abstract String getName();
 7 public abstract void setName(String paramString);
 8 public abstract Server getServer();
 9 public abstract void setServer(Server paramServer);
10 public abstract ClassLoader getParentClassLoader();
11 public abstract void setParentClassLoader(ClassLoader paramClassLoader);
12 public abstract void addConnector(Connector paramConnector);
13 public abstract Connector[] findConnectors();
14 public abstract void removeConnector(Connector paramConnector);
15 public abstract void addExecutor(Executor paramExecutor);
16 public abstract Executor[] findExecutors();
17 public abstract Executor getExecutor(String paramString);
18 public abstract void removeExecutor(Executor paramExecutor);
19 }

 

 

从 Service 接口中定义的方法中可以看出,它主要是为了关联 Connector 和 Container,同时会初始化它下面的其它组件,注意接口中它并没有规定一定要控制它下面的组件的生命周期。所有组件的生命周期在一个 Lifecycle 的接口中控制。Tomcat 中 Service 接口的标准实现类是 StandardService 它不仅实现了 Service 借口同时还实现了 Lifecycle 接口,这样它就可以控制它下面的组件的生命周期了。 StandardService 中主要的几个方法实现的代码,下面是 setContainer 和 addConnector 方法的源码:

StandardService.setContainer
 1 public void setContainer(Container container) {
 2       Container oldContainer = this.container;
 3       if ((oldContainer != null) && (oldContainer instanceof Engine))   //判断当前的这个 Service 有没有已经关联了 Container,如果已经关联了,那么去掉这个关联关系
 4           ((Engine) oldContainer).setService(null);
 5       this.container = container;
 6       if ((this.container != null) && (this.container instanceof Engine))
 7           ((Engine) this.container).setService(this);
 8       if (getState().isAvailable() && (this.container != null)) {
 9           try {
10               this.container.start();
11           } catch (LifecycleException e) {
12               // Ignore
13           }
14       }
15       if (getState().isAvailable() && (oldContainer != null)) {
16           try {
17               oldContainer.stop();          //如果这个 oldContainer 已经被启动了,结束它的生命周期
18           } catch (LifecycleException e) {
19               // Ignore
20           }
21       }
22       // Report this property change to interested listeners
23       support.firePropertyChange("container", oldContainer, this.container);
24   }

 

StandardService.addConnector
 1 public void addConnector(Connector connector) {
 2        synchronized (connectorsLock) {
 3            connector.setService(this);
 4            Connector results[] = new Connector[connectors.length + 1];
 5            System.arraycopy(connectors, 0, results, 0, connectors.length);
 6            results[connectors.length] = connector;
 7            connectors = results;
 8            if (getState().isAvailable()) {
 9                try {
10                    connector.start();
11                } catch (LifecycleException e) {
12                    log.error(sm.getString(
13                            "standardService.connector.startFailed",
14                            connector), e);
15                }
16            }
17            // Report this property change to interested listeners
18            support.firePropertyChange("connector", null, connector);
19        }
20    }

 

 

Server类是提供一个接口让其他程序能够访问到这个service集合,同时要维护它所包含的所有的Service的生命周期,包括如何初始化,如何结束服务,如何找到别人要访问的Service。它的标准实现类 StandardServer 实现server这些方法,同时也实现了LifecycleMBeanBase接口的所有方法,LifecycleMBeanBase接口实现了MBeanRegistration并继承了LifecycleBase接口。LifecycleBase接口实现了Lifecycle接口。所以Lifecycle接口的方法实现都在其它组件中,所以组件的生命周期由包含它的父组件控制。如Server的Start方法和Stop方法就会调用Service组件的相关方法。下面主要看一下 StandardServer 重要的一个方法 addService 的实现:

StandardServer.addService
 1 @Override
 2   public void addService(Service service) {
 3       service.setServer(this);  //关联Server和Service
 4       synchronized (servicesLock) {
 5           Service results[] = new Service[services.length + 1];
 6           System.arraycopy(services, 0, results, 0, services.length);
 7           results[services.length] = service;
 8           services = results;
 9           if (getState().isAvailable()) {
10               try {
11                   service.start();
12               } catch (LifecycleException e) {
13                   // Ignore
14               }
15           }
16           // Report this property change to interested listeners
17           support.firePropertyChange("service", null, service);
18       }
19   }

 

 

3. Tomcat重要配置文件Server.xml

3.1 server.xml

 Tomcat服务器是由一系列可配置的组件构成,其中核心的组件是Catalina Servlet容器,它是所有其他Tomcat组件的顶层容器。Tomcat的组件在<CATALINA_HOME>/conf/server.xml文件中进行配置,每个Tomcat组件在server.xml文件中对应一种配置元素。一下代码以XML的形式展示各种Tomcat组件之间的关系(以下是fat环境的tomcat的server.xml配置):

Server.xml
 1 <?xml version=‘1.0‘ encoding=‘utf-8‘?>
 2 <Server port="8005" shutdown="SHUTDOWN">
 3   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
 4   <Listener className="org.apache.catalina.core.JasperListener" />
 5   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
 6   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
 7   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
 8   <GlobalNamingResources>
 9     <Resource name="UserDatabase" auth="Container"
10               type="org.apache.catalina.UserDatabase"
11               description="User database that can be updated and saved"
12               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
13               pathname="conf/tomcat-users.xml" />
14   </GlobalNamingResources>
15   <Service name="Catalina">
16     <Connector port="8080" protocol="HTTP/1.1"
17                 socketBuffer="9000"
18                 enableLookups="false"
19                 tcpNoDelay="true"
20                 minSpareThreads="20"
21                 maxThreads="1024"
22                 connectionTimeout="5000"
23                 maxHttpHeaderSize="32768"
24                 acceptCount="150"
25                 redirectPort="8443" />
26     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
27     <Engine name="Catalina" defaultHost="localhost">
28       <Realm className="org.apache.catalina.realm.LockOutRealm">
29         <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
30                resourceName="UserDatabase"/>
31       </Realm>
32       <Host name="localhost"  appBase="/opt/ctrip/web/work"
33             unpackWARs="true" autoDeploy="true">
34       
35          <Context path="/btbjob" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>
36         <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/opt/logs/tomcat"
37                prefix="access." suffix=".log"
38                pattern="%h %l %u %t &quot;%r&quot; %s %b %T" />
39       </Host>
40     </Engine>
41   </Service>
42 </Server>

 

 

3.2 Server.xml元素详解

元素名

 

 

属性

 

 

解释

 

 

server

port

指定一个端口,这个端口负责监听关闭tomcat的请求

shutdown

指定向端口发送的命令字符串

service

name

指定service的名字

Connector(表示客户端和service之间的连接)

port

指定服务器端要创建的端口号,并在这个断口监听来自客户端的请求

protocol

浏览器请求必须为HTTP1.1

如果使用AJP处理器,该值必须为AJP/1.3

socketBuffer

设Socket输出缓冲区的大小(以字节为单位),-1表示禁止缓冲,默认值为9000字节

tcpNoDelay

为true时,可以提高性能。默认值为true

minSpareThreads

设当连接器第一次启协创建线程的数目,确保至少有这么多的空闲线程可用。默认值为4

maxHttpHeaderSize

HTTP请求和响应头的最大量,以字节为单位,默认值为4096字节

maxSpareThreads

允许存在空闲线程的最大数目,默认值为50

enableLookups

如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址

redirectPort

指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号

acceptCount

指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理

connectionTimeout

指定超时的时间数(以毫秒为单位)

Engine(表示指定service中的请求处理机,接收和处理来自Connector的请求)

defaultHost

指定缺省的处理请求的主机名,它至少与其中的一个host元素的name属性值是一样的

Context(表示一个web应用程序,通常为WAR文件,关于WAR的具体信息见servlet规范)

docBase

应用程序的路径或者是WAR文件存放的路径

path

表示此web应用程序的url的前缀,这样请求的url为http://localhost:8080/path/****

reloadable

这个属性非常重要,如果为true,则tomcat会自动检测应用程序的/WEB-INF/lib 和/WEB-INF/classes目录的变化,自动装载新的应用程序,我们可以在不重起tomcat的情况下改变应用程序

host(表示一个虚拟主机)

name

指定主机名

appBase

应用程序基本目录,即存放应用程序的目录

autoDeploy

指示Tomcat运行时,如有新的WEB程序加开appBase指定的目录下,是否为自动布署,默认值为true

unpackWARs

如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接从WAR文件中运行应用程序

Logger(表示日志,调试和错误信息)

className

指定logger使用的类名,此类必须实现org.apache.catalina.Logger 接口

prefix

指定log文件的前缀

suffix

指定log文件的后缀

timestamp

如果为true,则log文件名中要加入时间,如下例:localhost_log.001-10-04.txt

Realm(表示存放用户名,密码及role的数据库)

className

指定Realm使用的类名,此类必须实现org.apache.catalina.Realm接口

Valve(功能与Logger差不多,其prefix和suffix属性解释和Logger 中的一样)

className

指定Valve使用的类名,如用org.apache.catalina.valves.AccessLogValve类可以记录应用程序的访问信息

directory

指定log文件存放的位置

pattern

有两个值,common方式记录远程主机名或ip地址,用户名,日期,第一行请求的字符串,HTTP响应代码,发送的字节数。combined方式比common方式记录的值更多

3.4 配置HTTPS连接器

 

4. Tomcat部署方式

4.1 Tomcat部署

tomcat部署方式有三种:

  • 在tomcat中的conf目录,修改server.xml的<host/>节点添加<Context />配置.

    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true"> 
    </Context>

  • 将web项目文件拷贝到webapps目录下.
  • 在conf目录中,新建Catalina\localhost目录,在该目录创建一个xml文件,名字随意,只要和当前文件不重名,该xml文件的内容为:

    <Context path="/hello" docBase="D:eclipse3.2.2forwebtoolsworkspacehelloWebRoot" debug="0" privileged="true"> 
    </Context>
    注:一个Tomcat若要部署多个项目,在Server.xml文件添加<Context/>如:

     <Context path="/btbjob_one" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>
     <Context path="/btbjob_two" crossContext="true" reloadable="true" docBase="/opt/ctrip/web/jobws.btborder.flight.charter-jobws-btborder/work"/>

4.2 Tomcat热部署

     热部署是指在你对项目代码(不论是JSP、JAVA类,甚至是配置文件)进行了修改时,在不重启WEB服务器前提下能让修改生效。

  1.  修改Tomcat安装目录下的conf下的context.xml,在<Context>节点上添加reloadable="true"属性
  2.  

  3. 进入myEclipse - > preference -> Tomcat 7.x -> JDK , 在Optional Java VM arguments中填入 -Dcom.sun.management.jmxremote=true 如下图:

     技术分享

5. Tomcat 处理Http请求流程

Tomcat Server处理请求的过程,如下以HTTP请求为例:

技术分享

由上图可见,Request的解析和加工过程不是在一个方法里搞定,而是信息流动过程中逐步解析的,不同层次的处理器解析不同层次的信息,在解析过程同时做了些判断和拦截的工作,比如当发现是要访问WEB-INF的资源,会直接返回错误给客户端等等。

6. Tomcat开发中常见问题分享

6.1 Tomcat常见错误

tomcat启动时错误

1 The JAVA_HOME environment variable is not defined This environment variable is needed to run this program;

Re:没有在tomcat的配置文件.bash_profile中设置环境变量JAVA_HOME,具体设置方法为:加入如下几行:

   JAVA_HOME=/home/tomcat/j2sdk1.4.2_08(具体值要以实际的jdk安装路径为准)

   export JAVA_HOMECLASSPATH=/home/tomcat/j2sdk1.4.2_08/lib/tools.jar:/home/tomcat/j2sdk1.4.2_08/lib/dt.jar

   export CLASSPATH

2 Error occurred during initialization of VM Could not reserve enough space for object heap

Re:在tomcat的bin目录下,catalina.sh文件的tomcat内存参数配置过大,超过机器可用内存总数造成,修改到适当的值即可,修改的参数为:JAVA_OPTS="-Xms50m -Xmx60m"

3 tomcat启动时报某个目录没有权限,启动失败,或者不能执行某些jsp页

Re:tomcat需要tomcat用户具有一些目录和文件的相应权限, 所有目录应该具有读写执行(浏览)的权限,jsp,class文件应该最少具有读权限, 一些文件需要写权限,下面是已知的需要读写权限文件的列表:

       $CATALINA_HOME/logs下所有文件

       $CATALINA_HOME/work下所有文件

       $CATALINA_HOME/publish/main/count.txt文件

       $CATALINA_HOME/publish/chatroom/resource下的所有.xml文件

       所有上传图片目录都需要写权限。

       改变文件目录权限的方法:执行下面命令,设置所有的tomcat安装下的文件和目录,可以保证执行,但是不是很安全。

1. 设置tomcat上级目录/opt所有用户都有读写执行权限:

chmod 777 [tomcat的上级目录]

2. 设置tomcat目录下的所有文件的属主为deploy:

      chown -R tomcat [tomcat安装目录]

3. 设置所有tomcat下的所有文件和目录的属主(deploy)具有读写执行权限

chmod 700 -R /opt/tomcat

注意:公司测试环境tomcat所属主为deploy,切勿使用root主去启动tomcat.

4  执行startup.sh文件后告诉地址已经使用,导致tomcat不能启动

Re:可能是前一次执行./shutdown.sh文件关闭tomcat时没有停止已经启动的Java进程,而这个进程仍然在监听tomcat所使用的端口,或者有另外一个tomcat正在运行,server.xml文件中的监听端口和当前tomcat冲突。

   使用下面命令查看当前系统是否有正在监听的端口(8080--webcache, 90--www或http).

1. 列出所有系统正在监听的端口和绑定的地址netstat -l

2. 找出当前系统中的所有进程,管道符号过滤输出显示包含java字符串的行

     ps -A |grep java       

3. 结束一个指定的进程:

     kill   [进程号]

4. 强制杀死一个进程:

      kill -9 [进程号]

     如果确定不是别的tomcat运行导致的冲突或者没有java进程运行可以再次运行startup.sh文件启动tomcat

 tomcat运行时错误

1. org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (Network error IOException: Connection refused: connect)
2. Caused by: java.sql.SQLException: Network error IOException: Connection refused: connect
3. Caused by: java.net.ConnectException: Connection refused: connect

Re:数据库未开情况下运行tomcat出现的问题,把数据库打开就行了

4.java.lang.NoSuchMethodError: org.objectweb.asm.ClassVisitor.visit(IILjava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V
at net.sf.cglib.core.ClassEmitter.begin_class(ClassEmitter.java:77) 这个问题是最最常见的,第一次整合ssh的时候会发现这个问题,有时候删除掉相关的包还是会冲突。所以解决办法我一般是:

Re:把MyEclipse中的hibernate中的有关ASM的包全部删除,将spring中asm包拷贝进去重启就Ok了,有时候需要删除其他文件,到网上找找吧;

还有个解决办法:Spring 和 Hibernate 共用的一些 jar 文件发生了版本冲突, 删除 WEB-INF/lib/asm-2.2.3.jar 然后重启 Tomcat.


5 .javax.servlet.jsp.JspException: Invalid argument looking up property usersVO.account of bean loginForm

Re,一般提示这种错误表示VO中的form没有实例化,在reset方法中new一下就Ok了。养成良好的编码习惯可以避免这种简单错误。

6.严重: Exception loading sessions from persistent storage

Re:tomcat安装目录\work\Catalina\localhost\{webAppName}\SESSIONS.ser,删除此文件

6.2 Tomcat 常见的错误码

 出现404错误是因为你所请求的页面不可用!这是response对象的http响应中的状态行404表示当前请求的页面不可用!200表示测试通过。500表示服务器内部发生错误等!

具体错误码对用的信息参见:http://blog.csdn.net/lidawei201/article/details/8482006

6.3 Tomcat 开发中遇中文乱码问题

tomcat8之前的默认编码是ISO-8859-1,tomcat8的默认编码是UTF-8.

解决中文乱码的问题:如果请求方式GET.可以更改${CATALINA_HOME}/conf/server.xml中对应的Connector中指定URIEncoding="UTF-8"参数.

                                   如果请求方式POST.可以通过Filter来设置编码.Tomcat7.x (已经将这个Filter加入Tomcat内置了,直接复制一下代码到你的项目web.xml中)

 

 1 <filter>
 2 <filter-name>setCharacterEncodingFilter</filter-name>
 3 <filter-class>org.apache.catalina.filters.SetCharacterEncodingFilter</filter-class>
 4 <init-param>
 5   <param-name>encoding</param-name>
 6   <param-value>UTF-8</param-value>
 7 </init-param>
 8 </filter>
 9 <filter-mapping>
10 <filter-name>setCharacterEncodingFilter</filter-name>
11 <url-pattern>/*</url-pattern>
12 </filter-mapping>

 


如果这样设置还是有乱码问题,需要考虑下数据库的编码.

 

Tomcat