tomcat 解析(五)-Tomcat的核心组成和启动过程

声明:源码版本为Tomcat 6.0.35

前面的文章中介绍了Tomcat的基本配置,每个配置项也基本上对应了Tomcat的组件结构,如果要用一张图来形象展现一下Tomcat组成的话,整个Tomcat的组成可以如下图所示:

Tomcat在接收到用户请求时,将会通过以上组件的协作来给最终用户产生响应。首先是最外层的Server和Service来提供整个运行环境的基础设施,而Connector通过指定的协议和接口来监听用户的请求,在对请求进行必要的处理和解析后将请求的内容传递给对应的容器,经过容器一层层的处理后,生成最终的响应信息,返回给客户端。

Tomcat的容器通过实现一系列的接口,来统一处理一些生命周期相关的操作,而Engine、Host、Context等容器通过实现Container接口来完成处理请求时统一的模式,具体表现为该类容器内部均有一个Pipeline结构,实际的业务处理都是通过在Pipeline上添加Valve来实现,这样就充分保证整个架构的高度可扩展性。Tomcat核心组件的类图如下图所示:

在介绍请求的处理过程时,将会详细介绍各个组件的作用和处理流程。本文将会主要分析Tomcat的启动流程,介绍涉及到什么组件以及初始化的过程,简单期间将会重点分析HTTP协议所对应Connector启动过程。

Tomcat在启动时的重点功能如下:

  • 初始化类加载器:主要初始化CommonLoader、CatalinaLoader以及SharedLoader;
  • 解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化);
  • 初始化连接器:初始化声明的Connector,以指定的协议打开端口,等待请求。

不管是通过命令行启动还是通过Eclipse的WST server UI,Tomcat的启动流程是在org.apache.catalina.startup. Bootstrap类的main方法中开始的,在启动时,这个类的核心代码如下所示:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public static void main(String args[]) {

        if (daemon == null) {

            daemon = new Bootstrap();//实例化该类的一个实例

            try {

                daemon.init();//进行初始化

            catch (Throwable t) {

                ……;

            }

        }

        try {

    ……//此处略去代码若干行

    if (command.equals("start")) {

                daemon.setAwait(true);

                daemon.load(args);//执行load,生成组件实例并初始化

                daemon.start();//启动各个组件

            }

    ……//此处略去代码若干行

    }

从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

private void initClassLoaders() {

        try {

            commonLoader = createClassLoader("common"null);

            if( commonLoader == null ) {

                // no config file, default to this loader - we might be in a ‘single‘ env.

                commonLoader=this.getClass().getClassLoader();

            }

            catalinaLoader = createClassLoader("server", commonLoader);

            sharedLoader = createClassLoader("shared", commonLoader);

        catch (Throwable t) {

            log.error("Class loader creation threw exception", t);

            System.exit(1);

        }

    }

在以上的代码总,我们可以看到初始化了三个类加载器,这三个类加载器将会有篇博文进行简单的介绍。

然后我们进入Catalina的load方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

public void load() {

//……

        //初始化Digester组件,定义了解析规则

        Digester digester = createStartDigester();

        //……中间略去代码若干,主要作用为将server.xml文件转换为输入流

        try {

            inputSource.setByteStream(inputStream);

            digester.push(this);

//通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系

            digester.parse(inputSource);

            inputStream.close();

        catch (Exception e) {

          

        }

        // 调用Server的initialize方法,初始化各个组件

        if (getServer() instanceof Lifecycle) {

            try {

                getServer().initialize();

            catch (LifecycleException e) {

                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))

                    throw new java.lang.Error(e);

                else

                    log.error("Catalina.start", e);

                

            }

        }

    }

在以上的代码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的initialize方法。关于Digester组件的使用,后续会有一篇专门的博文进行讲解,而Server的initialize方法中,会发布事件并调用各个Service的initialize方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Connector的初始化,这可能是最值得关注的。

Connector的initialize方法,核心代码如下:

?


1

2

3

4

5

6

7

8

9

10

11

public void initialize() throws LifecycleException{

     //该适配器会完成请求的真正处理   

adapter = new CoyoteAdapter(this);

    //对于不同的实现,会有不同的ProtocolHandler实现类,我们来看    //Http11Protocol,它用来处理HTTP请求

        protocolHandler.setAdapter(adapter);

        try {

            protocolHandler.init();

        catch (Exception e) {

            ……

        }

    }

在Http11Protocol的init方法中,核心代码如下:

?


1

2

3

4

5

6

7

8

9

public void init() throws Exception {

        endpoint.setName(getName());//endpoint为JIoEndpoint的实现类

        endpoint.setHandler(cHandler);

        try {

            endpoint.init();//核心代码就是调用 JIoEndpoint的初始化方法

        catch (Exception ex) {

           ……

        }

    }

我们看到最终的初始化方法最终都会调到JIoEndpoint的init方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,所以后续的内容将会重点关注此类:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public void init() throws Exception {

        if (acceptorThreadCount == 0) {//接受请求的线程数

            acceptorThreadCount = 1;

        }

        if (serverSocket == null) {

            try {

                if (address == null) {

    //基于特定端口创建一个ServerSocket对象,准备接受请求

                    serverSocket = serverSocketFactory.createSocket(port, backlog);

                else {

                    serverSocket = serverSocketFactory.createSocket(port, backlog, address);

                }

            catch (BindException orig) {

             ……

            }

        }

    }

在上面的代码中,我们可以看到此时初始化了一个ServerSocket对象,用来准备接受请求。

如果将其比作赛跑,此时已经到了“各就各位”状态,就等最终的那声“发令枪”了,而Catalina的start方法就是“发令枪”啦:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public void start() {

        if (getServer() == null) {

            load();

        }

        if (getServer() == null) {

            log.fatal("Cannot start server. Server instance is not configured.");

            return;

        }

        if (getServer() instanceof Lifecycle) {

            try {

                ((Lifecycle) getServer()).start();

            catch (LifecycleException e) {

                log.error("Catalina.start: ", e);

            }

        }

      //……

 }

此时会调用Server的start方法,这里我们重点还是关注JIoEndpoint的start方法:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public void start()  throws Exception {

        if (!initialized) {

            init();

        }

        if (!running) {

            running = true;

            paused = false;

            if (executor == null) {

    //初始化处理连接的线程,maxThread的默认值为200,这也就是为什么    //说Tomcat只能同时处理200个请求的来历

                workers = new WorkerStack(maxThreads);

            }

            for (int i = 0; i < acceptorThreadCount; i++) {

    //初始化接受请求的线程

                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);

                acceptorThread.setPriority(threadPriority);

                acceptorThread.setDaemon(daemon);

                acceptorThread.start();

            }

        }

    }

从以上的代码,可以看到,如果没有在server.xml中声明Executor的话,将会使用内部的一个容量为200的线程池用来后续的请求处理。并且按照参数acceptorThreadCount的设置,初始化线程来接受请求。而Acceptor是真正的幕后英雄,接受请求并分派给处理过程:

?


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

protected class Acceptor implements Runnable {

        public void run() {

            while (running) {

                // 接受发送过来的请求

    Socket socket = serverSocketFactory.acceptSocket(serverSocket);

                    serverSocketFactory.initSocket(socket);

                    //处理这个请求

                    if (!processSocket(socket)) {

                        //关闭连接

                        try {

                            socket.close();

                        catch (IOException e) {

                            // Ignore

                        }

                    }

            }

        }

    }

从这里我们可以看到,Acceptor接受Socket请求,并调用processSocket方法来进行请求的处理。至此,Tomcat的组件整装待命,等待请求的到来。关于请求的处理,会在下篇文章中介绍。

时间: 2024-08-07 11:41:57

tomcat 解析(五)-Tomcat的核心组成和启动过程的相关文章

Tomcat请求处理过程(Tomcat源码解析五)

前面已经分析完了Tomcat的启动和关闭过程,本篇就来接着分析一下Tomcat中请求的处理过程. 在开始本文之前,咋们首先来看看一个Http请求处理的过程,一般情况下是浏览器发送http请求->建立Socket连接->通过Socket读取数据->根据http协议解析数据->调用后台服务完成响应,详细的流程图如上图所示,等读者读完本篇,应该就清楚了上图所表达的意思.Tomcat既是一个HttpServer也是一个Servlet 容器,那么这里必然也涉及到如上过程,首先根据HTTP协议

Tomcat系列(3)——Tomcat 组件及架构核心部分

1.架构图 2. 定义 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta 项目中的一个核心项目,它早期的名称为catalina,后来由Apache.Sun 和其他一些公司及个人共同开发而成,并更名为Tomcat.Tomcat 是一个小型的轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选,因为Tomcat 技术先进.性能

tomcat的启动过程(Tomcat源码解析(三))

Tomcat组件生命周期管理 在Tomcat总体结构 (Tomcat源代码解析之二)中,我们列出了Tomcat中Server,Service,Connector,Engine,Host,Context的继承关系图,你会发现它们都实现了org.apache.catalina.Lifecycle接口,而org.apache.catalina.util.LifecycleBase采用了模板方法模式来对所有支持生命周期管理的组件的生命周期各个阶段进行了总体管理,每个需要生命周期管理的组件只需要继承这个基

Tomcat架构解析(二)-----Connector、Tomcat启动过程以及Server的创建过程

Connector用于跟客户端建立连接,获取客户端的Socket,交由Container处理.需要解决的问题有监听.协议以及处理器映射等等. 一.Connector设计   Connector要实现的主要功能如下: 设计图如下: 1.ProtocolHandler Connector中的ProtocolHandler用于处理不同的通信协议,Tomcat主要支持HTTP.AJP协议,并且支持BIO.NIO.APR等I/O方式.ProtocolHandler中使用AbstractEndpoint启动

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

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

tomcat解析之简单web服务器(图)

链接地址:http://gogole.iteye.com/blog/587163 之前有javaeyer推荐了一本书<how tomcat works>,今天晚上看了看,确实不错,第一眼就着迷了. 于是乎就学着书上的例子敲了敲,学会了一个简单web服务器的大概实现,当然,这个简直就无法称之为web服务器,但是也算是走进web服务器的第一步吧. 这篇文章仅限于学习记录,文笔凌乱之处,还望各位见谅. OK,下面进入正题: 开始之前,首先我们要清楚以下几个内容. 首先,一个最简单服务器包括三个部分:

Tomcat 设计模式总结(Tomcat源代码阅读系列之八)

本篇我们将来分析一下Tomcat中所涉及到设计模式,本文我们将主要来分析外观模式,观察者模式,责任链模式,模板方法模式,命令模式. 在开始本文之前,笔者先说明一下对于设计模式的一点看法.笔者曾经经常看到网上有人讨论设计模式,也偶尔会遇到有人非要严格按照GOF设计模式的类图以及其中的角色去套用别人的设计,只要类图不一样,或者角色多了或者少了就会觉得怎么和官方定义的模式不一样,其实这都是对设计模式的误解.设计模式其实不仅仅存在软件行业,各行各业其实都有模式,它是所在行业对一些通用问题解决方案的总结和

Tomcat探秘(4):tomcat启动过程详述

熟悉Tomcat的工程师们,或者从事Java开发的,肯定都知道Tomcat是如何启动和停止的.在Tomcat源码包里面有个bin目录,该目录下放置了一些很重要的脚本,Tomcat启动和停止的脚本程序就放在这里,分别是startup.bat.shutdown.bat(Windows环境)和start.sh.shutdown.sh(Linux.Unix环境).大家一定都知道如何使用它们,接下来就是研究一下它们是如何实现启动和停止服务的. 在实际的生产环境下,绝大多数的Tomcat都是部署在Linux

Spring Boot启动过程(四):Spring Boot内嵌Tomcat启动

之前在Spring Boot启动过程(二)提到过createEmbeddedServletContainer创建了内嵌的Servlet容器,我用的是默认的Tomcat. private void createEmbeddedServletContainer() { EmbeddedServletContainer localContainer = this.embeddedServletContainer; ServletContext localServletContext = getServ