`
outlaw
  • 浏览: 30660 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

[Tomcat源码系列]结构解析 3)请求处理控制结构

阅读更多

一、请求处理控制结构基础
     与生命期结构类似,请求处理也是一个两层的结构
1.Valve:Valve是最小的处理单元,我们看看Valve的定义

A Valve is a request processing component associated with a particular Container. A series of Valves are generally associated with each other into a Pipeline.

   下面是Valve的接口定义

Java代码 复制代码

 

public interface Valve {
public String getInfo();
public Valve getNext();
public void setNext(Valve valve);
public void backgroundProcess();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void event(Request request, Response response, CometEvent event)
throws IOException, ServletException;

 

   其中我们最需要关注的是invoke方法,请求处理处理方法,我们看看tomcat对这个方法的注释(如果扩展Valve,必须仔细阅读)

Perform request processing as required by this Valve.An individual Valve <b>MAY</b> perform the following actions, in the specified order:
  • Examine and/or modify the properties of the specified Request and Response.
  • Examine the properties of the specified Request, completely generate the corresponding Response, and return control to the caller.
  • Examine the properties of the specified Request and Response, wrap either or both of these objects to supplement their functionality, and pass them on.
  • If the corresponding Response was not generated (and control was not returned, call the next Valve in the pipeline (if there is one) by executing context.invokeNext(). Examine, but not modify, the properties of the resulting Response (which was created by a subsequently invoked Valve or Container).
A Valve MUST NOT do any of the following things:
  • Change request properties that have already been used to direct the flow of processing control for this request (for instance,trying to change the virtual host to which a Request should be sent from a pipeline attached to a Host or Context in the standard implementation).
  • Create a completed Response AND pass this Request and Response on to the next Valve in the pipeline.
  • Consume bytes from the input stream associated with the Request,unless it is completely generating the response, or wrapping the request before passing it on.
  • Modify the HTTP headers included with the Response after the invokeNext() method has returned.
  • Perform any actions on the output stream associated with the specified Response after the invokeNext()method has returned.


2.Pipeline:如上所说,“A series of Valves are generally associated with each other into a Pipeline”,我们Tomcat对Pipeline接口的说明

写道
Interface describing a collection of Valves that should be executed in sequence when the invoke() method is invoked. It is required that a Valve somewhere in the pipeline (usually the last one) must process the request and create the corresponding response, rather than trying to pass the request on.

    与其说是Pipeline(管道),其实此处似乎称为处理链更合适一点,中间每个处理节点(Valve)做一部分处理,然后由下一个处理节点继续处理,而通常最后一个处理节点必须处理请求并创建响应。如下是Pipeline接口的定义

Java代码 复制代码

 

public interface Pipeline {
    public Valve getBasic();
    public void setBasic(Valve valve);
    public void addValve(Valve valve);
    public Valve[] getValves();
    public void removeValve(Valve valve);
    public Valve getFirst();
}

 

二、请求处理过程解析
1.处理过程预览
    Tomcat的主要处理组件Engine、Host、Context和Wrapper的实现都会实现Pipeline接口(实际是ContainerBase实现了该接口)。在第一篇中,我们知道,实际对请求的处理是一个Adpater,Tomcat中Adapter的实现是CoyoteAdapter,因此请求处理的入口是CoyoteAdapter的service方法,我们的请求处理过程就从CoyoteAdapter开始

1. CoyoteAdapter.service
   --组装好请求处理链
   --StandardEngine. getPipeline().getFirst().invoke(request, response);
       --XxxValve.invoke
       --StandardEngineValve.invoke
2. StandardEngineValve.invoke
   --Host. getPipeline().getFirst().invoke(request, response);
      --YyyValve.invoke
      --StandardHostValve.invoke
3. StandardHostValve.invoke
  --Context. getPipeline().getFirst().invoke(request, response);
     --ZzzValve.invoke
     --StandardContextValve.invoke
4. StandardContextValve.invoke
    --ServletRequestListener. requestInitialized
    --Wrapper. getPipeline().getFirst().invoke(request, response);
          --StandardWrapperValve.invoke
    -- ServletRequestListener. requestDestroyed
5. StandardWrapperValve.invoke
    --组装Filter+Servlet
    --处理请求

 

2.    处理解析
1)我们从CoyoteAdapter.service为入口,看看Tomcat的整个请求处理过程
CoyoteAdapter.service

Java代码 复制代码

 

    public void service(org.apache.coyote.Request req, 
    	                org.apache.coyote.Response res)
        throws Exception {
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);

        if (request == null) {
             //创建request、response对象
             ...略
        }
       
        try {
            // Parse and set Catalina and configuration specific
            // request parameters
            req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());
            //组装请求处理链在此部分进行,后面详细解析
            if (postParseRequest(req, request, res, response)) {
                // Calling the container
                connector.getContainer().getPipeline().getFirst().invoke(request, response); //此处的Container是StandardEngine对象

                ...略

        } catch (IOException e) {
            ;
        } catch (Throwable t) {
            log.error(sm.getString("coyoteAdapter.service"), t);
        } finally {
            ...略
        }
    }

 

  2)默认的StandardEngine这个Pipeline会有StandardEngineValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元

写道
<!-- The request dumper valve dumps useful debugging information about
the request and response data received and sent by Tomcat.
Documentation at: /docs/config/valve.html -->
<Valve className="org.apache.catalina.valves.RequestDumperValve"/>

我们看看StandardEngineValve.invoke

Java代码 复制代码

 

   public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Select the Host to be used for this Request
        //请求是属于哪个Host的在CoyoteAdapter.postParseRequest已经准备好,后面重点会讲解这个过程
        Host host = request.getHost();
        if (host == null) {
            response.sendError
                (HttpServletResponse.SC_BAD_REQUEST,
                 sm.getString("standardEngine.noHost",
                              request.getServerName()));
            return;
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);

    }

 

3)同样的,StandardHost这个Pipeline会有StandardHostValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元

<Valve className="org.apache.catalina.authenticator.SingleSignOn" />
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>

StandardHostValve如何处理请求跟StandardEngineValve类似,接下来请求进入到StandardContextValve.invoke
4) 同样的,StandardContext这个Pipeline会有StandardContextValve这个处理单元。我们可以配置其他的处理单元到处理链中,譬如Tomcat就定义了如下的处理单元

写道
<Valve className="org.apache.catalina.valves.AccessLogValve"
prefix="localhost_access_log." suffix=".txt"
pattern="common"/>

我们看看StandardContextValve是如何处理请求的

Java代码 复制代码

    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Disallow any direct access to resources under WEB-INF or META-INF
        MessageBytes requestPathMB = request.getRequestPathMB();
        if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/META-INF"))
            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))
            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {
            String requestURI = request.getDecodedRequestURI();
            notFound(requestURI, response);
            return;
        }

        // Wait if we are reloading
        while (context.getPaused()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                ;
            }
        }

        // Select the Wrapper to be used for this Request
        Wrapper wrapper = request.getWrapper();
        if (wrapper == null) {
            String requestURI = request.getDecodedRequestURI();
            notFound(requestURI, response);
            return;
        }

//ServletRequestListener. requestInitialized
...略

        wrapper.getPipeline().getFirst().invoke(request, response);

//ServletRequestListener.requestDestroyed
...略
       }

5) 同样的,StandardWrapper这个Pipeline会有StandardWrapperValve这个处理单元,详细处理过程可以参阅一下org.apache.catalina.core.StandardWrapperValve.invoke代码
3.请求处理链组装
      在如上代码中可以看到,实际请求进入到StandardEngineValve的时候,由哪个Host、哪个Context、哪个Wrapper参与处理过程实际已经确定下来了,我们看看这个过程是如何处理的。Tomcat使用org.apache.tomcat.util.http.mapper.Mapper来管理这种请求如何映射到具体Host、Context、Wrapper。这个过程分为两个阶段
1)初始化阶段
    在Engine、Host、Context的初始化阶段,会将子组件通过addChild方法加入到父组件中,我们以StandardContext.addChild为例看看如何处理

Java代码 复制代码

 

   public void addChild(Container child) {

        // Global JspServlet
        Wrapper oldJspServlet = null;

        if (!(child instanceof Wrapper)) {
            throw new IllegalArgumentException
                (sm.getString("standardContext.notWrapper"));
        }

        Wrapper wrapper = (Wrapper) child;
        boolean isJspServlet = "jsp".equals(child.getName());

        // Allow webapp to override JspServlet inherited from global web.xml.
        if (isJspServlet) {
            oldJspServlet = (Wrapper) findChild("jsp");
            if (oldJspServlet != null) {
                removeChild(oldJspServlet);
            }
        }

        String jspFile = wrapper.getJspFile();
        if ((jspFile != null) && !jspFile.startsWith("/")) {
            if (isServlet22()) {
                if(log.isDebugEnabled())
                    log.debug(sm.getString("standardContext.wrapper.warning",
                                       jspFile));
                wrapper.setJspFile("/" + jspFile);
            } else {
                throw new IllegalArgumentException
                    (sm.getString("standardContext.wrapper.error", jspFile));
            }
        }

        super.addChild(child);

        if (isJspServlet && oldJspServlet != null) {
            /*
             * The webapp-specific JspServlet inherits all the mappings
             * specified in the global web.xml, and may add additional ones.
             */
            String[] jspMappings = oldJspServlet.findMappings();
            for (int i=0; jspMappings!=null && i<jspMappings.length; i++) {
                addServletMapping(jspMappings[i], child.getName());
            }
        }
    }

在如上处理过程当中,我们关注的重点是addServletMapping,进一步进入addServletMapping,我们可以看到最终会将Wrapper对象告诉给Mapper对象,代码如下

Java代码 复制代码
public void addServletMapping(String pattern, String name,
                                  boolean jspWildCard) {
        // Validate the proposed mapping
        if (findChild(name) == null)
            throw new IllegalArgumentException
                (sm.getString("standardContext.servletMap.name", name));
        pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));
        if (!validateURLPattern(pattern))
            throw new IllegalArgumentException
                (sm.getString("standardContext.servletMap.pattern", pattern));

        // Add this mapping to our registered set
        synchronized (servletMappings) {
            String name2 = (String) servletMappings.get(pattern);
            if (name2 != null) {
                // Don't allow more than one servlet on the same pattern
                Wrapper wrapper = (Wrapper) findChild(name2);
                wrapper.removeMapping(pattern);
                mapper.removeWrapper(pattern);
            }
            servletMappings.put(pattern, name);
        }
        Wrapper wrapper = (Wrapper) findChild(name);
        wrapper.addMapping(pattern);

        // Update context mapper
        mapper.addWrapper(pattern, wrapper, jspWildCard);

        fireContainerEvent("addServletMapping", pattern);

    }

    StandardEngine和StandardHost也会有类似的处理,这里不再重复,可以直接看这两个类的addChild实现
2)请求处理链组装阶段
   在如上分析CoyoteAdpater.service的过程当中,我们知道,在进入StandardEngineValve.invoke之前,会先把请求处理链先准备好,实际上有了Mapper这个对象及如上的基础,这个处理过程不会太过复杂。代码处理过程是CoyoteAdpater.service-->CoyoteAdapter. postParseRequest-->Mapper.mapper,有兴趣可以直接看org.apache.tomcat.util.http.mapper.Mapper.mapper方法
三、通过如上层层处理,最终请求到达我们实际的处理Servlet

分享到:
评论

相关推荐

    tomcat源码解析

    ### tomcat源码解析 #### 简介与概览 Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用的开发与部署环境中。它不仅支持Servlet API,还支持JSP规范,使得开发者能够轻松地构建动态网页。本文旨在深入...

    Tomcat源码研究.pdf

    在Tomcat源码中,Catalina的脚本解析是了解其启动流程的关键。首先,Tomcat会解析conf/server.xml文件,该文件中定义了所有容器组件的配置。脚本解析的过程涉及到几个关键类,如Digester,它负责将XML文件中的配置...

    tomcat6源码分析

    1. 初始化:Tomcat启动时,会读取配置文件server.xml,解析配置信息,构建出服务器的结构。 2. 加载Web应用:根据context.xml配置加载Web应用,创建对应的Context对象。 3. 初始化Servlet:调用Servlet的init()方法...

    tomcat3源码包

    《深入剖析Tomcat3:源码解析与运行机制》 Tomcat3作为Apache Tomcat的早期版本,虽然相比后来的6、7、8等版本在功能上显得较为简单,但正是这种简洁,使得它成为初学者研究Web服务器和Java Servlet容器理想的起点...

    tomcat源码基于6.0

    《深入剖析Tomcat源码:基于6.0版本》 Tomcat是一款开源的、轻量级的Java Servlet容器,它负责解析HTTP请求并调用Java应用程序。Tomcat 6.0是其历史上的一个重要版本,提供了许多关键特性和改进。通过深入研究其...

    tomcat源码

    【Tomcat源码详解】 Tomcat,作为Apache软件基金会的项目之一,是广泛使用的Java Servlet容器,它实现了Java EE的Web应用服务器规范。Tomcat6.0版本是历史上非常流行的一个版本,对于开发者来说,深入理解其源码有...

    tomcat5 源码

    Coyote处理与客户端的HTTP连接,解析请求并生成响应。它包括两个主要部分:Connector(处理连接)和ProtocolHandler(处理特定的网络协议,如HTTP/1.1)。 5. **Apr库** Apache Portable Runtime(Apr)提供了...

    tomcat5.5.29源码下载(含方法)

    1. **目录结构**:Tomcat源码的根目录包含了许多子目录,如`bin`(启动脚本)、`conf`(配置文件)、`webapps`(默认应用程序)、`work`(工作目录)等。了解这些目录的作用对于管理和调试Tomcat非常重要。 2. **...

    tomcat 7.0源码

    2. **解析请求**:Coyote将网络数据解析为`org.apache.coyote.Request`和`org.apache.coyote.Response`对象。 3. **请求分发**:请求通过`org.apache.catalina.connector.Request`类进行进一步处理,找到对应的...

    学习tomcat源码+英文《How Tomcat Work》和每一章的相关项目+tomcat6源码依赖jar

    在深入探讨Tomcat源码之前,我们先了解一下Tomcat是什么。Tomcat是一款开源的Java Servlet容器,由Apache软件基金会开发,它实现了Java EE中的Web应用服务器部分,特别是Servlet和JavaServer Pages (JSP)规范。《How...

    tomcat4.0源码

    当一个HTTP请求到达时,Coyote解析请求并将其传递给Catalina。Catalina根据请求的URL定位到对应的Context,进一步找到Servlet实例,执行Servlet的service()方法。如果涉及到JSP,Jasper会介入将JSP编译成Servlet并...

    《深入剖析TOMCAT》中文版的源码

    深入学习Tomcat源码,有助于我们掌握以下关键知识点: 1. **Servlet生命周期**:理解Servlet的初始化、服务、销毁过程,以及Tomcat如何管理和调度Servlet实例。 2. **容器结构**:Tomcat的容器模型包括Engine...

    深入剖析Tomcat源码

    1. **Tomcat架构**:了解Tomcat的基本结构,包括Catalina、Coyote、Jasper等核心组件,以及它们在请求处理中的角色和交互方式。 2. **Servlet与JSP解析**:学习JSP如何被编译成Servlet,以及Servlet生命周期管理,...

    Tomcat源码apache-tomcat-8.5.47-src.zip

    在开始学习Tomcat源码之前,首先需要了解一些基本概念。Java Servlet是Java平台上的一个标准接口,用于处理HTTP请求。而JSP则是用于创建动态网页的Java技术,它将业务逻辑和页面展示分离。Tomcat作为Servlet容器,...

    tomcat6.0.35源码

    用户只需导入必要的jar包,即可使用Ant工具对Tomcat源码进行编译,这为开发者提供了极大的便利。 在探索Tomcat6.0.35源码时,你可以关注以下几个关键知识点: 1. **Servlet容器**:Tomcat作为Servlet容器,负责...

    Tomcat8.0底层源码

    《深入剖析Tomcat 8.0底层源码》 Tomcat作为一款广泛应用的开源Java Servlet容器,其8.0版本的源码对于开发者来说是一份宝贵的教育资源。通过深入学习Tomcat 8.0的源码,我们可以理解Web服务器的工作原理,提升对...

    tomcat-7.0.42源码

    1. Catalina:这是Tomcat的核心,负责管理Servlet容器,解析并执行请求。Catalina的启动过程涉及Server、Service、Engine、Host和Context等层次结构,这些元素在web.xml配置文件中定义。 2. Jasper:Jasper是Tomcat...

    tomcat-7.0.90-src-源码

    深入Tomcat源码,我们可以学习到以下关键知识点: 1. **Web应用部署**:Tomcat如何解析`WEB-INF/web.xml`配置文件,加载Servlet和Filter,以及如何根据`META-INF/context.xml`设置上下文参数。 2. **生命周期管理*...

Global site tag (gtag.js) - Google Analytics