[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代码 [Tomcat源码系列]结构解析 3)请求处理控制结构

 

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:
  • Examineand/ormodifythepropertiesofthespecifiedRequestandResponse.
  • ExaminethepropertiesofthespecifiedRequest,completelygeneratethecorrespondingResponse,andreturncontroltothecaller.
  • ExaminethepropertiesofthespecifiedRequestandResponse,wrapeitherorbothoftheseobjectstosupplementtheirfunctionality,andpassthemon.
  • IfthecorrespondingResponsewasnotgenerated(andcontrolwasnotreturned,callthenextValveinthepipeline(ifthereisone)byexecutingcontext.invokeNext().Examine,butnotmodify,thepropertiesoftheresultingResponse(whichwascreatedbyasubsequentlyinvokedValveorContainer).
AValveMUSTNOTdoanyofthefollowingthings:
  • 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代码 [Tomcat源码系列]结构解析 3)请求处理控制结构

 

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代码 [Tomcat源码系列]结构解析 3)请求处理控制结构

 

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

therequestandresponsedatareceivedandsentbyTomcat.

Documentationat:/docs/config/valve.html-->

<Valve classname="org.apache.catalina.valves.RequestDumperValve"/>

我们看看StandardEngineValve.invoke

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

 

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" />

<Valveclassname="org.apache.catalina.valves.AccessLogValve"directory="logs"

prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>

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

写道
<Valve classname="org.apache.catalina.valves.AccessLogValve"

prefix="localhost_access_log."suffix=".txt"

pattern="common"/>

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

Java代码 [Tomcat源码系列]结构解析 3)请求处理控制结构
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代码 [Tomcat源码系列]结构解析 3)请求处理控制结构

 

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代码 [Tomcat源码系列]结构解析 3)请求处理控制结构
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

相关推荐