请求处理控制结构

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

文章分类:Java编程

一、请求处理控制结构基础
     与生命期结构类似,请求处理也是一个两层的结构
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代码
  1. public   interface  Valve {  
  2. public  String getInfo();  
  3. public  Valve getNext();  
  4. public   void  setNext(Valve valve);  
  5. public   void  backgroundProcess();  
  6. public   void  invoke(Request request, Response response)  
  7. throws  IOException, ServletException;  
  8. public   void  event(Request request, Response response, CometEvent event)  
  9. throws  IOException, ServletException;  
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代码
  1. public   interface  Pipeline {  
  2.     public  Valve getBasic();  
  3.     public   void  setBasic(Valve valve);  
  4.     public   void  addValve(Valve valve);  
  5.     public  Valve[] getValves();  
  6.     public   void  removeValve(Valve valve);  
  7.     public  Valve getFirst();  
  8. }  
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代码
  1. public   void  service(org.apache.coyote.Request req,   
  2.                     org.apache.coyote.Response res)  
  3.     throws  Exception {  
  4.     Request request = (Request) req.getNote(ADAPTER_NOTES);  
  5.     Response response = (Response) res.getNote(ADAPTER_NOTES);  
  6.   
  7.     if  (request ==  null ) {  
  8.          //创建request、response对象   
  9.          ...略  
  10.     }  
  11.      
  12.     try  {  
  13.         // Parse and set Catalina and configuration specific   
  14.         // request parameters   
  15.         req.getRequestProcessor().setWorkerThreadName(Thread.currentThread().getName());  
  16.         //组装请求处理链在此部分进行,后面详细解析   
  17.         if  (postParseRequest(req, request, res, response)) {  
  18.             // Calling the container   
  19.             connector.getContainer().getPipeline().getFirst().invoke(request, response); //此处的Container是StandardEngine对象   
  20.   
  21.             ...略  
  22.   
  23.     } catch  (IOException e) {  
  24.         ;  
  25.     } catch  (Throwable t) {  
  26.         log.error(sm.getString("coyoteAdapter.service" ), t);  
  27.     } finally  {  
  28.         ...略  
  29.     }  
  30. }  
    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代码
  1. public   final   void  invoke(Request request, Response response)  
  2.      throws  IOException, ServletException {  
  3.   
  4.      // Select the Host to be used for this Request   
  5.      //请求是属于哪个Host的在CoyoteAdapter.postParseRequest已经准备好,后面重点会讲解这个过程   
  6.      Host host = request.getHost();  
  7.      if  (host ==  null ) {  
  8.          response.sendError  
  9.              (HttpServletResponse.SC_BAD_REQUEST,  
  10.               sm.getString("standardEngine.noHost" ,  
  11.                            request.getServerName()));  
  12.          return ;  
  13.      }  
  14.   
  15.      // Ask this Host to process this request   
  16.      host.getPipeline().getFirst().invoke(request, response);  
  17.   
  18.  }  
   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代码
  1.      public   final   void  invoke(Request request, Response response)  
  2.         throws  IOException, ServletException {  
  3.   
  4.         // Disallow any direct access to resources under WEB-INF or META-INF   
  5.         MessageBytes requestPathMB = request.getRequestPathMB();  
  6.         if  ((requestPathMB.startsWithIgnoreCase( "/META-INF/"0 ))  
  7.             || (requestPathMB.equalsIgnoreCase("/META-INF" ))  
  8.             || (requestPathMB.startsWithIgnoreCase("/WEB-INF/"0 ))  
  9.             || (requestPathMB.equalsIgnoreCase("/WEB-INF" ))) {  
  10.             String requestURI = request.getDecodedRequestURI();  
  11.             notFound(requestURI, response);  
  12.             return ;  
  13.         }  
  14.   
  15.         // Wait if we are reloading   
  16.         while  (context.getPaused()) {  
  17.             try  {  
  18.                 Thread.sleep(1000 );  
  19.             } catch  (InterruptedException e) {  
  20.                 ;  
  21.             }  
  22.         }  
  23.   
  24.         // Select the Wrapper to be used for this Request   
  25.         Wrapper wrapper = request.getWrapper();  
  26.         if  (wrapper ==  null ) {  
  27.             String requestURI = request.getDecodedRequestURI();  
  28.             notFound(requestURI, response);  
  29.             return ;  
  30.         }  
  31.   
  32. //ServletRequestListener. requestInitialized   
  33. ...略  
  34.   
  35.         wrapper.getPipeline().getFirst().invoke(request, response);  
  36.   
  37. //ServletRequestListener.requestDestroyed   
  38. ...略  
  39.        }  
    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代码
  1. public   void  addChild(Container child) {  
  2.   
  3.      // Global JspServlet   
  4.      Wrapper oldJspServlet = null ;  
  5.   
  6.      if  (!(child  instanceof  Wrapper)) {  
  7.          throw   new  IllegalArgumentException  
  8.              (sm.getString("standardContext.notWrapper" ));  
  9.      }  
  10.   
  11.      Wrapper wrapper = (Wrapper) child;  
  12.      boolean  isJspServlet =  "jsp" .equals(child.getName());  
  13.   
  14.      // Allow webapp to override JspServlet inherited from global web.xml.   
  15.      if  (isJspServlet) {  
  16.          oldJspServlet = (Wrapper) findChild("jsp" );  
  17.          if  (oldJspServlet !=  null ) {  
  18.              removeChild(oldJspServlet);  
  19.          }  
  20.      }  
  21.   
  22.      String jspFile = wrapper.getJspFile();  
  23.      if  ((jspFile !=  null ) && !jspFile.startsWith( "/" )) {  
  24.          if  (isServlet22()) {  
  25.              if (log.isDebugEnabled())  
  26.                  log.debug(sm.getString("standardContext.wrapper.warning" ,  
  27.                                     jspFile));  
  28.              wrapper.setJspFile("/"  + jspFile);  
  29.          } else  {  
  30.              throw   new  IllegalArgumentException  
  31.                  (sm.getString("standardContext.wrapper.error" , jspFile));  
  32.          }  
  33.      }  
  34.   
  35.      super .addChild(child);  
  36.   
  37.      if  (isJspServlet && oldJspServlet !=  null ) {  
  38.          /*  
  39.           * The webapp-specific JspServlet inherits all the mappings  
  40.           * specified in the global web.xml, and may add additional ones.  
  41.           */   
  42.          String[] jspMappings = oldJspServlet.findMappings();  
  43.          for  ( int  i= 0 ; jspMappings!= null  && i<jspMappings.length; i++) {  
  44.              addServletMapping(jspMappings[i], child.getName());  
  45.          }  
  46.      }  
  47.  }  
   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代码
  1. public   void  addServletMapping(String pattern, String name,  
  2.                                   boolean  jspWildCard) {  
  3.         // Validate the proposed mapping   
  4.         if  (findChild(name) ==  null )  
  5.             throw   new  IllegalArgumentException  
  6.                 (sm.getString("standardContext.servletMap.name" , name));  
  7.         pattern = adjustURLPattern(RequestUtil.URLDecode(pattern));  
  8.         if  (!validateURLPattern(pattern))  
  9.             throw   new  IllegalArgumentException  
  10.                 (sm.getString("standardContext.servletMap.pattern" , pattern));  
  11.   
  12.         // Add this mapping to our registered set   
  13.         synchronized  (servletMappings) {  
  14.             String name2 = (String) servletMappings.get(pattern);  
  15.             if  (name2 !=  null ) {  
  16.                 // Don't allow more than one servlet on the same pattern   
  17.                 Wrapper wrapper = (Wrapper) findChild(name2);  
  18.                 wrapper.removeMapping(pattern);  
  19.                 mapper.removeWrapper(pattern);  
  20.             }  
  21.             servletMappings.put(pattern, name);  
  22.         }  
  23.         Wrapper wrapper = (Wrapper) findChild(name);  
  24.         wrapper.addMapping(pattern);  
  25.   
  26.         // Update context mapper   
  27.         mapper.addWrapper(pattern, wrapper, jspWildCard);  
  28.   
  29.         fireContainerEvent("addServletMapping" , pattern);  
  30.   
  31.     }  
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值