zoukankan      html  css  js  c++  java
  • Tomcat

    1. 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目录中,新建Catalinalocalhost目录,在该目录创建一个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. 进入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安装目录workCatalinalocalhost{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>

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

  • 相关阅读:
    android自动登录
    【199】ArcGIS 添加自定义工具到工具箱
    【198】Synergy
    【197】PowerShell 通过 FTP 下载文件
    【196】Dell 移动工作站系统安装方法
    php如何同时连接多个数据库
    FreeRTOS学习笔记——任务间使用队列同步数据
    牛腩新闻发布系统之发布
    Linux散列表(二)——宏
    Excel导入数据库(三)——SqlBulkCopy
  • 原文地址:https://www.cnblogs.com/xxuan/p/6743295.html
Copyright © 2011-2022 走看看