tomcat——概述

http://gearever.iteye.comhtml

大致架构java

 

Tomcat由一系列逻辑模块组织而成,这些模块主要包括: web

  • 核心架构模块,例如Server,Service,engine,host和context及wrapper等
  • 网络接口模块connector
  • log模块
  • session管理模块
  • jasper模块
  • naming模块
  • JMX模块
  • 权限控制模块

Service中配置了实际工做的Engine,同时配置了用来处理时间业务的线程组Executor(若是没有配置则用系统默认的WorkThread模式的线程组),以及处理网络socket的相关组件connector。apache

1) Executor是线程池,它的具体实现是java的concurrent包实现的executor,这个不是必须的,若是没有配置,则使用自写的worker thread线程池 
2) Connector是网络socket相关接口模块,它包含两个对象,ProtocolHandler及Adapter 数组

  • ProtocolHandler是接收socket请求,并将其解析成HTTP请求对象,能够配置成nio模式或者传统io模式
  • Adapter是处理HTTP请求对象,它就是从StandEngine的valve一直调用到StandWrapper的valve

 

容器类:tomcat提供四种容器,继承自同一个容器基类,有标准实现,也能够定制化。tomcat

  • Engine:org.apache.catalina.core.StandardEngine
  • Host: org.apache.catalina.core.StandardHost
  • Context:org.apache.catalina.core.StandardContext
  • Wrapper:org.apache.catalina.core.StandardWrapper

engine

  • Cluster: 实现tomcat集群,例如session共享等功能,经过配置server.xml能够实现,对其包含的全部host里的应用有效,该模块是可选的。其实现方式是基于pipeline+valve模式的,有时间会专门整理一个pipeline+valve模式应用系列(这篇文章非原创,原做者会整理);
  • Realm:实现用户权限管理模块,例如用户登陆,访问控制等,经过经过配置server.xml能够实现,对其包含的全部host里的应用有效,该模块是可选的;
  • Pipeline:这里简单介绍下,以后会有专门文档说明。每一个容器对象都有一个pipeline,它不是经过server.xml配置产生的,是必须有的。它就是容器对象实现逻辑操做的骨架,在pipeline上配置不一样的valve,当须要调用此容器实现逻辑时,就会按照顺序将此pipeline上的全部valve调用一遍,这里能够参考责任链模式;
  • Valve:实现具体业务逻辑单元。能够定制化valve(实现特定接口),而后配置在server.xml里。对其包含的全部host里的应用有效。定制化的valve是可选的,可是每一个容器有一个缺省的valve,例如engine的StandardEngineValve,是在StandardEngine里自带的,它主要实现了对其子host对象的StandardHostValve的调用,以此类推。

配置例子安全

<Engine name="Catalina" defaultHost="localhost">     
  <Valve className="MyValve0"/>     
  <Valve className="MyValve1"/>     
  <Valve className="MyValve2"/>     
  ……     
  <Host name="localhost" appBase="webapps">   
  </Host>  
</Engine>

运行环境中,pipeline上的valve数组按照配置的顺序加载,可是不管有无配置定制化的valve或有多少定制化的valve,每一个容器缺省的valve,例如engine的StandardEngineValve,都会在数组中最后一个。 服务器

host

StandardHost的核心模块与StandardEngine差很少。只是做用域不同,它的模块只对其包含的子context有效。除此,还有一些特殊的逻辑,例如context的部署。Context的部署仍是比较多的,主要分为:网络

  • War部署
  • 文件夹部署
  • 配置部署等

Context
Context是host的子容器,它是wrapper容器的集合。

应该说StandardContext是tomcat中最大的一个类。它封装的是每一个web app。
看一下StandardContext的主要逻辑单元概念图。session

  • Manager: 它主要是应用的session管理模块。其主要功能是session的建立,session的维护,session的持久化(persistence),以及跨context的session的管理等。Manager模块能够定制化,tomcat也给出了一个标准实现;

          org.apache.catalina.session.StandardManager  


manager模块是必需要有的,能够在server.xml中配置,若是没有配置的话,会在程序里生成一个manager对象。

  • Resources: 它是每一个web app对应的部署结构的封装,好比,有的app是tomcat的webapps目录下的某个子目录或是在context节点配置的其余目录,或者是war文件部署的结构等。它对于每一个web app是必须的。
  • Loader:它是对每一个web app的自有的classloader的封装。具体内容涉及到tomcat的classloader体系,会在一篇文档中单独说明。Tomcat正是有一套完整的classloader体系,才能保证每一个web app或是独立运营,或是共享某些对象等等。它对于每一个web app是必须的。
  • Mapper:它封装了请求资源URI与每一个相对应的处理wrapper容器的映射关系。

app>web.xml:

<servlet>  
  <servlet-name>httpserver</servlet-name>  
  <servlet-class>com.gearever.servlet.TestServlet</servlet-class>  
</servlet>  
   
<servlet-mapping>  
  <servlet-name>httpserver</servlet-name>  
  <url-pattern>/*.do</url-pattern>  
</servlet-mapping>

对于mapper对象,能够抽象的理解成一个map结构,其key是某个访问资源,例如/*.do,那么其value就是封装了处理这个资源TestServlet的某个wrapper对象。当访问/*.do资源时,TestServlet就会在mapper对象中定位到。这里须要特别说明的是,经过这个mapper对象定位特定的wrapper对象的方式,只有一种状况,那就是在servlet或jsp中经过forward方式访问资源时用到。例如,

request.getRequestDispatcher(url).forward(request, response)  

关于mapper机制会在一篇文档中专门说明,这里简单介绍一下,方便理解。如图所示。

Mapper对象在tomcat中存在于两个地方(注意,不是说只有两个mapper对象存在),其一,是每一个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;其二,是connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪一个host下的哪一个context的哪一个wrapper容器。
这样,经过上面说的forward方式访问资源会用到第一种mapper,除此以外,其余的任何方式,都是经过第二种方式的mapper定位到wrapper来处理的。也就是说,forward是服务器内部的重定向,不须要通过网络接口,所以只须要经过内存中的处理就能完成。这也就是常说的forward与sendRedirect方式重定向区别的根本所在。
看一下request.getRequestDispatcher(url) 方法的源码。

Request
    /**
     * @return a RequestDispatcher that wraps the resource at the specified
     * path, which may be interpreted as relative to the current request path.
     *
     * @param path Path of the resource to be wrapped
     */
    @Override
    public RequestDispatcher getRequestDispatcher(String path) {

        Context context = getContext();
        if (context == null) {
            return null;
        }

        // If the path is already context-relative, just pass it through
        if (path == null) {
            return null;
        } else if (path.startsWith("/")) {
            return (context.getServletContext().getRequestDispatcher(path));
        }

        // Convert a request-relative path to a context-relative one
        String servletPath = (String) getAttribute(
                RequestDispatcher.INCLUDE_SERVLET_PATH);
        if (servletPath == null) {
            servletPath = getServletPath();
        }

        // Add the path info, if there is any
        String pathInfo = getPathInfo();
        String requestPath = null;

        if (pathInfo == null) {
            requestPath = servletPath;
        } else {
            requestPath = servletPath + pathInfo;
        }

        int pos = requestPath.lastIndexOf('/');
        String relative = null;
        if (context.getDispatchersUseEncodedPaths()) {
            if (pos >= 0) {
                relative = URLEncoder.DEFAULT.encode(
                        requestPath.substring(0, pos + 1), "UTF-8") + path;
            } else {
                relative = URLEncoder.DEFAULT.encode(requestPath, "UTF-8") + path;
            }
        } else {
            if (pos >= 0) {
                relative = requestPath.substring(0, pos + 1) + path;
            } else {
                relative = requestPath + path;
            }
        }

        return context.getServletContext().getRequestDispatcher(relative);
    }

Wrapper
Wrapper是context的子容器,它封装的处理资源的每一个具体的servlet。

主要说说servlet对象与servlet stack对象。这两个对象在wrapper容器中只存在其中之一,也就是说只有其中一个不为空。当以servlet对象存在时,说明此servlet是支持多线程并发访问的,也就是说不存在线程同步的过程,此wrapper容器中只包含一个servlet对象(这是咱们经常使用的模式);当以servlet stack对象存在时,说明servlet是不支持多线程并发访问的,每一个servlet对象任一时刻只有一个线程能够调用,这样servlet stack实现的就是个简易的线程池,此wrapper容器中只包含一组servlet对象,它的基本原型是worker thread模式实现的。 


那么,怎么来决定是以servlet对象方式存储仍是servlet stack方式存储呢?其实,只要在开发servlet类时,实现一个SingleThreadModel接口便可。

public class LoginServlet extends HttpServlet implements javax.servlet.SingleThreadModel

可是值得注意的是,这种同步机制只是从servlet规范的角度来讲提供的一种功能,在实际应用中并不能彻底解决线程安全问题,例如若是servlet中有static数据访问等,所以若是对线程安全又比较严格要求的,最好仍是用一些其余的自定义的解决方案。

Wrapper的基本功能已经说了。那么再说一个wrapper比较重要的概念。严格的说,并非每个访问资源对应一个wrapper对象。而是每一种访问资源对应一个wrapper对象。其大体可分为三种:

  • 处理静态资源的一个wrapper:例如html,jpg等静态资源的wrapper,它包含了一个tomcat的实现处理静态资源的缺省servlet:

         org.apache.catalina.servlets.DefaultServlet  

 

  • 处理jsp的一个wrapper:例如访问的全部jsp文件,它包含了一个tomcat的实现处理jsp的缺省servlet:

          org.apache.jasper.servlet.JspServlet  


它主要实现了对jsp的编译等操做

  • 处理servlet的若干wrapper:它包含了自定义的servlet对象,就是在web.xml中配置的servlet。


须要注意的是,前两种wrapper分别是一个,主要是其对应的是DefaultServlet及JspServlet。这两个servlet是在tomcat的全局conf目录下的web.xml中配置的,当app启动时,加载到内存中。

tomcat conf web.xml
<servlet>  
  <servlet-name>default</servlet-name>  
  <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>  
  <init-param>  
    <param-name>debug</param-name>  
    <param-value>0</param-value>  
  </init-param>  
  <init-param>  
    <param-name>listings</param-name>  
    <param-value>false</param-value>  
  </init-param>  
  <load-on-startup>1</load-on-startup>  
</servlet>  
   
<servlet>  
  <servlet-name>jsp</servlet-name>  
  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>  
  <init-param>  
    <param-name>fork</param-name>  
    <param-value>false</param-value>  
  </init-param>  
  <init-param>  
    <param-name>xpoweredBy</param-name>  
    <param-value>false</param-value>  
  </init-param>  
  <load-on-startup>3</load-on-startup>  
</servlet>
相关文章
相关标签/搜索