`
outlaw
  • 浏览: 29594 次
  • 性别: 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

分享到:
评论

相关推荐

    JAVA上百实例源码以及开源项目源代码

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    JAVA上百实例源码以及开源项目

     Java 3DMenu 界面源码,有人说用到游戏中不错,其实平时我信编写Java应用程序时候也能用到吧,不一定非要局限于游戏吧,RES、SRC资源都有,都在压缩包内。 Java zip压缩包查看程序源码 1个目标文件 摘要:Java源码...

    开涛高可用高并发-亿级流量核心技术

    3.11.1 请求解析和业务处理线程池分离 57 3.11.2 业务线程池隔离 58 3.11.3 业务线程池监控/运维/降级 58 3.11.4 如何使用Servlet 3异步化 59 3.11.5 一些Servlet 3异步化压测数据 64 4 限流详解 66 4.1 限流算法 67...

    java开源包8

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包3

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    Java Web编程宝典-十年典藏版.pdf.part2(共2个)

    是PDF电子书,不是源码。共分2个包。 《Java Web编程宝典(十年典藏版)》是一本集技能、范例、项目和应用为一体的学习手册,书中介绍了应用Java Web进行程序开发的各种技术、技巧。全书分4篇,共24章,其中,第1篇为...

    java开源包1

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包11

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包2

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包6

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包5

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包10

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包4

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包7

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包9

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    java开源包101

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    Java资源包01

    Tomcat Native 这个项目可以让 Tomcat 使用 Apache 的 apr 包来处理包括文件和网络IO操作,以提升性能。 预输入搜索 Cleo Cleo 是一个灵活的软件库用于处理一些预输入和自动完成的搜索功能,该项目是 LinkedIn 公司...

    超级有影响力霸气的Java面试题大全文档

    finally是异常处理语句结构的一部分,表示总是执行。 finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 16、sleep...

    java 面试题 总结

    finally是异常处理语句结构的一部分,表示总是执行。 finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 13、sleep()...

Global site tag (gtag.js) - Google Analytics