How Tomcat Works 5

上一节简单介绍了tomcat中的流水线,当connector收到一条消息的时候,将socket交给processor来处理,processor构造出来request和response对象并解析http请求,然后processor调用container的的invoke方法来处理这两个对象。invoke方法是父类ContainerBase中的方法,主要是调用该Container对应的Pipeline处理请求。Tomcat中StandardEngine对应的pipeline是StandardPipeline,其中的basicValve是StandardEngineValve,每个basicValve都是该容器中最后被调用的valve。它其中的一个任务便是调用自己下层组件的invoke方法,继续流水线的处理,直到最后的的wrappervalve。值得提一点的是一个servlet对应一个wrapper。流程图如下:

本节重点介绍的是tomcat处理一个请求的大体过程。

在tomcat的外层web.xlm会配置一个默认的serverlet——org.apache.catalina.servlets.DefaultServlet当根据mapping未匹配到需要处理的servlet的时候,会使用该servlet处理请求,DefaultServlet在tomcat启动的时候便会被加载。

假设我们访问的地址为http://localhost:8080/myapp/primi myapp是我自己写的一个app,非常简单的结构如下:

在WEB-INF下只有一个web.xml,配置了servlet的映射关系

  <servlet>
    <servlet-name>PrimitiveServlet</servlet-name>
    <servlet-class>PrimitiveServlet</servlet-class>
    <init-param>
      <param-name>debug</param-name>
      <param-value>2</param-value>
    </init-param>
  </servlet>

  <!-- Define the Manager Servlet Mapping -->

  <servlet-mapping>
    <servlet-name>PrimitiveServlet</servlet-name>
    <url-pattern>/primi</url-pattern>
  </servlet-mapping>

那么下面就来看看tomcat是如何一步步找到这个servlet并处理我们的请求的。

connector将收到的请求交给Engine来处理,一个在Engine的标准valve中会调用下面的方法来匹配到对应的host

// Select the Host to be used for this Request
        StandardEngine engine = (StandardEngine) getContainer();
        Host host = (Host) engine.map(request, true);
        if (host == null) {
            ((HttpServletResponse) response.getResponse()).sendError
                (HttpServletResponse.SC_BAD_REQUEST,
                 sm.getString("standardEngine.noHost",
                              request.getRequest().getServerName()));
            return;
        }

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

可以看到通过engine的map方法找到对应的host,其中在tomcat4版本中匹配url的过程是通过Mapper类完成的,这里会匹配到标识为localhost的Host容器,也就是所有对localhost的访问都会由localhost容器处理,获取到对应的Host后调用它的invoke方法,接下来的处理过程类似上面。

// Select the Context to be used for this Request
        StandardHost host = (StandardHost) getContainer();
        Context context = (Context) host.map(request, true);
        if (context == null) {
            ((HttpServletResponse) response.getResponse()).sendError
                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                 sm.getString("standardHost.noContext"));
            return;
        }

Context对应的就是webapps目录下一个个的应用程序,由Host匹配合适Context的方法在StandardHostMapper类中:

public Container map(Request request, boolean update) {
        // Has this request already been mapped?
        if (update && (request.getContext() != null))
            return (request.getContext());

        // Perform mapping on our request URI
        String uri = ((HttpRequest) request).getDecodedRequestURI();
        Context context = host.map(uri);

        // Update the request (if requested) and return the selected Context
        if (update) {
            request.setContext(context);
            if (context != null)
                ((HttpRequest) request).setContextPath(context.getPath());
            else
                ((HttpRequest) request).setContextPath(null);
        }
        return (context);

    }

获取到uri,该例子中为/myapp/primi。由于我们在server.xml中配置了localhost下有一个名字为myapp的Context,所以按照从后往前按照反斜杠/截取的过程,最终会匹配到mayapp这个context,同样类似上面的处理过程,context的invoke方法会被调用。也就是请求和响应消息被传递给名字为myapp的context来处理。

StandardContextValve中队消息的处理代码如下:

Context context = (Context) getContainer();

        // Select the Wrapper to be used for this Request
        Wrapper wrapper = null;
        try {
            wrapper = (Wrapper) context.map(request, true);
        } catch (IllegalArgumentException e) {
            badRequest(requestURI,
                       (HttpServletResponse) response.getResponse());
            return;
        }
        if (wrapper == null) {
            notFound(requestURI, (HttpServletResponse) response.getResponse());
            return;
        }

        // Ask this Wrapper to process this Request
        response.setContext(context);

        wrapper.invoke(request, response);

上面的主要逻辑是在一个应用上下文中匹配出对该消息处理的servlet,这里是一个Wrapper。由于我们设置了servletmapping,因此会匹配到StandardWrapper[PrimitiveServlet]这个wrapper。最后会由StandardWrapperValve处理该消息,主要逻辑如下:

servlet = wrapper.allocate();

上面是非常重要的一个步骤,就是获取一个对应的Servlet实例,allocate的主要逻辑如下:

if (!singleThreadModel) {

            // Load and initialize our instance if necessary
            if (instance == null) {
                synchronized (this) {
                    if (instance == null) {
                        try {
                            instance = loadServlet();
                        } catch (ServletException e) {
                            throw e;
                        } catch (Throwable e) {
                            throw new ServletException
                                (sm.getString("standardWrapper.allocate"), e);
                        }
                    }
                }
            }

            if (!singleThreadModel) {
                if (debug >= 2)
                    log("  Returning non-STM instance");
                countAllocated++;
                return (instance);
            }

        }

该方法中会判断一个Servlet是否实现了单线程的模式,如果不是单线程的模式每次都会返回相同的实例,也就是只有一个实例存在。生成servlet的方法由loadServlet使用类加载器加载对应的Servelet,加载过程我们前面几节已经涉及到了,这里不再赘述(针对servlet的init和service等方法的调用是在loadServlet方法中完成的)。如果是单线程模式会将特定数量的实例放到对象池中,每次从池中获取可用的对象,如果池中没有可用的实例,此时会阻塞,可以看出单线程模式会有性能问题。

到此针对一个请求寻找到合适的servlet处理的过程大概就如上面所讲。

时间: 2024-10-03 04:49:40

How Tomcat Works 5的相关文章

How Tomcat Works 2

上一节(How Tomcat Works 1 编写一个简单静态web服务器)编写了一个简单的web服务器,只能处理静态的资源,本节将继续向前迈出一个小步,创建两个不同的servlet容器,能够利用servlet简单的处理动态内容.注意每节的代码都是基于上一节的继续丰富,因此有必要从第一节开始看起. 在编写代码之前,需要先大体了解一下Servlet是什么,方便后面的理解,下面就是一个最简单的Servlet什么也没做: package prymont; import java.io.IOExcept

how tomcat works 读书笔记四 tomcat的默认连接器

其实在第三章,就已经有了连接器的样子了,不过那只是一个学习工具,在这一章我们会开始分析tomcat4里面的默认连接器. 连接器 Tomcat连接器必须满足以下几个要求 1 实现org.apache.cataline.Connector接口 2 负责创建实现了org.apache.cataline.Request接口的request对象 3 负责创建实现了org.apache.cataline.Response接口的response对象 这里默认的连接器的原理很简单,就是等待http请求,创建re

《How Tomcat Works》读书笔记(二)

<How Tomcat Works>读书笔记(二) 这是<How Tomcat Works>第一二章的读书笔记.第一张主要写了一个静态资源处理的web服务器,第二章加了对servlet的处理. 1. 概述 1.1 架构 HttpServer:表示Http服务器,与客户端通信,处理Http请求. StaticResourceProcessor:对静态资源请求进行处理. ServletProcessor:对Servlet资源请求进行处理. Request:表示Http请求,实现了Ser

How Tomcat works — 四、tomcat启动(3)

上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看container. 目录 Pipeline和Vavle StandardEngine类和StandardHost类 StandardContext类 总结 Pipeline和Vavle 在第二节(How Tomcat works — 二.tomcat启动(1))中没有介绍关于Pipeline和Vavle,因

how tomcat works 读书笔记(二)----------一个简单的servlet容器

app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/article/details/39378157) 回顾我们上一章,我们开发了一个最最简单的web服务器,它可以使用户访问服务器内的静态资源.当然这是远远不够的,在这一节里,我们就试着让服务器在能相应静态资源的基础上继续支持servlet. servlet接口 javax.servlet.Servlet接口

how tomcat works

目标 深入剖析tomcat •Java Web 服务性能优化实践2014-11-04阅读225 •How Tomcat work之第十五章之Digester之setting property2014-07-30阅读253 •How Tomcat work 之第十四章之server and services 之application2014-07-29阅读194 •how tomcat work之第十四章:server and service2014-07-27阅读352 •How Tomcat

读了两章的 How Tomcat Works

周一发现了一本书.How Tomcat Works 惯例先到豆瓣搜书评.结果书评出奇的好.然后下载了PDF.从简介中看,本书的每个章节都会不断的围绕怎么建造一个Tomcat讲解.我本人比较喜欢这种造轮子的书.原因很简单,比较笨.如果书中没有代码,很难从脑海中构件代码结构.可是万万没想到,看到第三章,就发现,书中的代码不全,完整的代码还要到GitHub下载.无奈只能放下这本好书.(个人觉得看书就是要了解本质,如果书中的源码不全,而是给出片段,剩下的还要到项目中找,我觉得你还不如让我去直接看Tomc

How Tomcat works — 五、tomcat启动(4)

前面摆了三节的姿势,现在终于要看到最终tomcat监听端口,接收请求了. 目录 Connector Http11Protocol JIoEndpoint 总结 在前面的初始化都完成之后,进行Connector的初始化,也是执行一些生命周期方法. Connector 在启动过程中这个类的主要作用是初始化并启动CoyoteAdapter和Http11Protocol: initInternal 新建一个CoyoteAdapter,并调用protocolHandler.setAdapter方法设置ad

How tomcat works 读书笔记十七 启动tomcat 上

一路跋山涉水,这是最后一章了. 关于tomcat的启动,有两个类,一个是Catalina类,一个是Bootstrap类. 理论上,两个类可以和到一起,但是为了支持多种运行模式,又把他们分开了. 为了让用户更方便的启动tomcat,还有批处理文件startup.bat(终于见到最顶层的模块了!!) Catalina 先说Catalina 它里面有一个Server组件(不懂的看第14章).同时在它的start方法中包含一个Digester对象(不懂的看第15章)用来解析conf/下的Server.x

攻城狮在路上(伍)How tomcat works(三)Tomcat的默认连接器

在第4章中将通过剖析Tomcat4的默认连接器的代码,讨论需要什么来创建一个真实的Tomcat连接器. 注意:本章中提及的“默认连接器”是指Tomcat4的默认连接器.即使默认的连机器已经被弃用,被更快的,代号为Coyote的连接器所代替,它仍然是一个很好的学习工具. Tomcat连接器是一个可以插入servlet容器的独立模块,已经存在相当多的连接器了,包括Coyote, mod_jk, mod_jk2和mod_webapp.一个Tomcat连接器必须符合以下条件: 1. 必须实现接口org.