Bootstrap

Tomcat(架构篇)

目录

一、Tomcat源码环境搭建

1. 下载Tomcat源代码

2. 搭建Tomcat源码环境

3. 源码中部署 web 项目

二、Tomcat 架构基础

1. HTTP工作流程

2. HTTP服务器请求处理

3. Tomcat核心功能

3.1. Tomcat核心功能

3.2. Tomcat服务器支持的IO模型及其协议。

3.3. Tomcat支持的应用层协议:

4. 连接器的组件

4.1. EndPoint组件

4.2. Processor组件

4.3. ProtocolHandler组件

4.4. Adapter组件

5. Catalina

5.1. 架构关系图

5.2. Catalina组件结构

6. Container容器结构

6.1. 简介

6.2. 各个组件的含义

6.3. Tomcat是怎么管理这些容器的呢

三、Tomcat架构源码解析

1. Tomcat 源码准备工作

1.1. Tomcat启动时序图

1.2. 生命周期Lifecycle接口

1.3. Tomcat各个组件的默认实现

1.4. ProtocolHandler接口及其实现

2. Tomcat 初始化源码解析

3. Server初始化服务器解析

3.1. 服务初始化源代码

3.2. Server类图

4. Service初始化服务解析

4.1. 启动执行service初始化操作

4.2. Service的类图

5. Executor线程池与Connector连接器解析初始化

5.1. 配置的线程池及其使用线程

5.2. 使用线程池

5.3. 使用JDK自带的jconsole工具查看线程信息。

5.4. Connector的作用

5.5. 网络通信

5.6. 选择IO模型

5.7. 协议处理器初始化

5.8. 端点初始化

5.9. 绑定IO模型

5.10. 绑定NIO模型

6. Tomcat启动源码解析

6.1. 当所有的组件全部初始化完成后,下面需要让所有的组件启动

6.2. 执行Catalina对象的start方法

6.3. Catalina启动

6.4. StandardServer启动

6.5. StandardService的启动方法

6.6. Connector连接器启动

6.7. 协议处理器启动

6.8. 端点启动

6.9. 具体的IO模型启动

6.10. NioEndpoint的启动方法中,创建线程去接收和处理请求

6.11. 启动Acceptor和Poller线程来接收和处理客户端发送过来的请求

6.12. 使用Acceptor用来接收客户端请求

6.13. 使用Poller线程来处理读写操作

6.14. StandardEngine启动代码

6.15. 启动子容器

6.16. 启动子容器的方法

6.17. 通过JDK本地线程池,启动线程的方式启动子容器。执行AbstractExecutorService类的submit方法

6.18. 执行JDK的线程代码

6.19. StandardHost启动

6.20. StandardContext启动

四、待更新 


一、Tomcat源码环境搭建

1. 下载Tomcat源代码

Tomcat官方网站,下载对应的源代码:Index of /dist/tomcat/tomcat-8

Tomcat查看组件官网文档地址:Apache Tomcat 8 Architecture (8.0.53) - Architecture Overview

2. 搭建Tomcat源码环境

下载后解压zip压缩包

创建catalina-home文件夹,然后把conf和webapps文件夹拷贝到 catalina-home文件夹中

在根目录下创建pom.xml的配置文件,导入如下的坐标依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.apache.tomcat</groupId>
    <artifactId>Tomcat</artifactId>
    <name>Tomcat</name>
    <version>8.0</version>
    <build>
        <finalName>Tomcat8.0</finalName>
        <sourceDirectory>java</sourceDirectory>
        <testSourceDirectory>test</testSourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <testResources>
            <testResource>
                <directory>test</directory>
            </testResource>
        </testResources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
 
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.3</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
    </dependencies>
 
</project>

配置启动类,截图如下:

配置jvm的启动参数。

-Dcatalina.home=C:/workspace\framework_workspace/apache-tomcat-8.0.53-src/catalina-home
-Dcatalina.base=C:/workspace\framework_workspace/apache-tomcat-8.0.53-src/catalina-home
-Djava.endorsed.dirs=C:/workspace\framework_workspace/apache-tomcat-8.0.53-src/catalina-home/endorsed
-Djava.io.tmpdir=C:/workspace\framework_workspace/apache-tomcat-8.0.53-src/catalina-home/temp
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=C:/workspace\framework_workspace/apache-tomcat-8.0.53-src/catalina-home/conf/logging.properties

此时可以启动服务器。

在启动的过程中, Tomcat源码util.TestCookieFilter类会报错,将其注释即可。

打开该类,把所有的代码注释掉即可。

若运行main方法之后,访问页面,出现如下图所以错误,

是由于启动org.apache.catalina.startup.Bootstrap的时候没有加载org.apache.jasper.servlet.JasperInitializer,

从而无法编译JSP。

解决办法

在tomcat的源码org.apache.catalina.startup.ContextConfig中的configureStart函数中手动将JSP解析器初

始化:

添加代码:

context.addServletContainerInitializer(new JasperInitializer(), null);

3. 源码中部署 web 项目

第一种方式:可以把web项目拷贝到webapps目录下。

第二种方式:可以在server.xml配置文件中配置Context标签

<!--部署web项目-->
<Context docBase="C:\workspace\tx_web1_war_exploded" path="/myweb" />

第三种方式:可以在Catalina/localhost 创建 xxx.xml配置文件,配置Context标签,部署项目。

具体的配置

<?xml version='1.0' encoding='utf-8'?>
<Context docBase="C:\workspace\tx_web1_war_exploded" reloadable="true"/>

二、Tomcat 架构基础

1. HTTP工作流程

HTTP协议是浏览器与服务器之间的数据传送协议。

作为应用层协议, HTTP是基于TCP/IP协议来传递数据的(HTMI文件、图片、查询结果等) ,

HTTP协议不涉及数据包( Packet )传输,主要规定了客户端和服务器之间的通信格式。

2. HTTP服务器请求处理

浏览器发给服务端的是一个HTTP格式的请求 , HTTP服务器收到这个请求后,需要调用服务端程序来处理,

所谓的服务端程序就是你写的Java类,一般来说不同的请求需要由不同的Java类来处理。

Tomcat架构设计

HTTP服务器不直接调用业务类,而是把请求交给容器来处理,容器通过servlet接口调用业务类。

因此servlet接口和Servlet容器的出现,达到了HTTP服务器与业务类解耦的目的。

而servlet接口和servlet容器这一 整套规范叫作servlet规范。

Tomcat按照servlet规范的要求实现了servlet容器,同时它们也具有HTTP服务器的功能。

作为Java程序员 ,如果我们要实现新的业务功能,只需要实现一个servlet,

并把它注册到tomcat ( servlet容器)中,剩下的事情就由Tomcat帮我们处理了。

3. Tomcat核心功能

3.1. Tomcat核心功能

Tomcat核心功能

处理socket连接 ,负责网络字节流与Request和Response对象的转化。

加载和管理servlet ,以及具体处理Request请求。

因此Tomcat设计了两个核心组件连接器( Connector )和容器( Container )来分别做这两件事情。

连接器负责对外交流,容器负责内部处理。

3.2. Tomcat服务器支持的IO模型及其协议。

Tomcat服务器支持的模型及其协议。

Tomcat8.0版本支持的IO模型(自8.5/9.0版本起,Tomcat移除了对BIO的支持) :

  • BIO 阻塞I/O
  • NIO 非阻塞I/O,采用Java NIO类库实现。
  • NIO2 异步I/O,采用JDK 7最新的NIO2类库实现。
  • APR采用Apache可移植运行库实现,是C/C++编写的本地库。如果选择该方案,需要单独安装APR库。

3.3. Tomcat支持的应用层协议:

Tomcat支持的应用层协议

HTTP/1.1 这是大部分web应用采用的访问协议。

AJP 用于和web服务器集成(如Apache), 以实现对静态资源的优化以及集群部署,当前支持AJP/1.3。

HTTP/2 HTTP 2.0大幅度的提升了web性能。下一代HTTP协议,自8 .5以及9.0版本之后支持。

4. 连接器的组件

连接器组件介绍及其作用

4.1. EndPoint组件

EndPoint组件通信端点,即通信监听的接口,是具体socket接收和发送处理器,是对传输层的抽象。

因此EndPoint用来实现TCP/ IP协议的。

Tomcat并没有EndPoint接口,而是提供了一 个抽象类AbstractEndpoint,里面定义了两个内部类: Acceptor和socketprocessor

Acceptor用于监听socket连接请求。socketprocessor用于处理接收到的socket请求 ,它实现Runnable接口,

在Run方法里调用协议处理组件processor进行处理。为了提高处理能力, socketprocessor被提交到线程池来

执行。

而这个线程池叫作执行器Executor

4.2. Processor组件

Processor组件协议处理接口, 如果说EndPoint是用来实现TCP/IP协议的,那么Processor用来实现HTTP协

议,Processor接收来自EndPoint的socket ,读取字节流解析成Tomcat Request和Response对象 ,并通过

Adapter将其提交到容器处理,Processor是对应用层协议的抽象。

4.3. ProtocolHandler组件

ProtocolHandler协议接口,通过Endpoint和Processor ,实现针对具体协议的处理能力。

Tomcat按照协议和I/O提供了6个实现类: AjpNioprotocol ,AjpAprProtocol、AjpNio2Protocol、

Http11NioProtocol、HttpllNio2Protocol、Httpl1AprProtocol.

我们在配置tomcat/conf/server .xml时,至少要指定具体的ProtocolHandler,

当然也可以指定协议名称,如: HTTP/1.1 , 如果安装了APR,那么将使用Http11AprProtocol,否则使用

Http11NioProtocol。

4.4. Adapter组件

Adapter组件由于协议不同,客户端发过来的请求信息也不尽相同, Tomcat定义了自己的Request类来“存放”

这些请求信息。

ProtocolHandler接口负责解析请求并生成Tomcat Request类。

但是这个Request对象不是标准的servletRequest ,也就意味着,不能用Tomcat Request作为参数来调用容器。

Tomcat设计者的解决方案是引入coyoteAdapter,这是适配器模式的经典运用,连接器调用coyoteAdapter的

Sevice方法,传入的是Tomcat Request对象, CoyoteAdapter负责将Tomcat Request转成servletRequest,再调

用容器的Service方法。

5. Catalina

5.1. 架构关系图

5.2. Catalina组件结构

1. Catalina

负责解析tomcat的配置文件,以此来创建服务器server组件,并根据命令来对其进行管理。

2. Server

服务器表示整个Catalina Servlet容器以及其它组件,负责组装并启动servlet引擎, Tomcat连接器。

server通过实现Lifecycle接口,提供了一种优雅的启动和关闭整个系统的方式

3. Service

服务是server内部的组件, 一server包含多个service.

它将若干个Connector组件绑定到一个Container ( Engine)上。

4. Connector

连接器,处理与客户端的通信,它负责接收客户请求,然后转给相关的容器处理,最后向客户返回响应结果。

5. Container

容器,负责处理用户的servlet请求,并返回对象给web用户的模块。

6. Container容器结构

6.1. 简介

Tomcat设计了4种容器,分别是Engine、Host、Context和wrapper.

这4种容器不是平行关系,而是父子关系。

Tomcat通过一种分层的架构,使得servlet容器具有很好的灵活性

6.2. 各个组件的含义

1. Engine

表示整个Catalina的servlet引擎,用来管理多个虚拟站点, 一个service最多只能有一个Engine ,但是一个引擎可包含多个Host。

2. Host

代表一个虚拟主机,或者说一个站点,可以给Tomcat配置多个虚拟主机地址,而一个虚拟主机下可包含多个Context。

3. Context

表示一个web应用程序, 一个Web应用可包含多个Wrapper。

4. Wrapper

表示一个servlet , Wrapper作为容器中的最底层,不能包含子容器。

6.3. Tomcat是怎么管理这些容器的呢

你会发现这些容器具有父子关系,形成一个树形结构 ,你可能马上就想到了设计模式中的组合模式。

没错, Tomcat就是用组合模式来管理这些容器的。

具体实现方法是,所有容器组件都实现了container接口.

因此组合模式可以使得用户对单容器对象和组合容器对象的使用具有一致性。

这里单容器对象指的是最底层的wrapper ,组合容器对象指的是上面的Context、Host或者Engine。

三、Tomcat架构源码解析

1. Tomcat 源码准备工作

1.1. Tomcat启动时序图

1.2. 生命周期Lifecycle接口

由于所有的组件均存在初始化、启动、停止等生命周期方法.拥有生命周期管理的特性,

所以Tomcat在设计的时候, 基于生命周期管埋抽象成了一个接口Lifecycle ,

而组件Server、 Service、Container、 Executor、 Connector 组件,

都实现了一个生命周期的接口,从而具有了以下生命周期中的核心方法:

  1. init() :初始化组件
  2. start() :启动组件
  3. stop() :停止组件
  4. destroy() : 销毁组件

1.3. Tomcat各个组件的默认实现

上面我们提到的Server、Service、 Engine、 Host、 Context都是接口,

下图中罗列了这些接口的默认实现类。

当前对于Endpoint组件来说,在Tomcat中没有对应的Endpoint接口,但是有一个抽象类AbstractEndpoint ,

其下有三个实现类: NioEndpoint、Nio2Endpoint、AprEndpoint

1.4. ProtocolHandler接口及其实现

2. Tomcat 初始化源码解析

步骤一:通过启动脚本的方式找入口,打开startup.bat文件

步骤二:根据代码内容打开 Catalina.bat 文件

步骤三:找到启动类 org.apache.catalina.startup.Bootstrap,执行该类的main方法。

步骤四:然后调用init方法,初始化Catalina对象。

步骤五:执行load方法

底层是通过反射的方式去执行Catalina的load方法。

步骤六:执行Catalina类的load方法,会解析server.xml配置文件,

构建Server Service等对象,构建完成后,准备执行初始化的方法。

步骤七:解析server.xml配置文件,构建StandardServer等对象。

步骤八:构建对象

步骤九:构建对象源码

3. Server初始化服务器解析

3.1. 服务初始化源代码

此处使用的是模板方法设计模式

3.2. Server类图

Server是Tomcat最顶层的容器,代表着整个服务器

Tomcat中其标准实现:

org.apache.catalina.core.StandardServer类

StandardServer继承结构类图如上图:

除了实现Server接口还需要继承Lifecycle

好处:

生命周期统一接口Lifecycle把所有的启动、停止、关闭等都放在一起统一管理

4. Service初始化服务解析

4.1. 启动执行service初始化操作

4.2. Service的类图

Service

一个Tomcat包含多个Service

Tomcat中其标准实现

org.apache.catalina.core.StandardService类

StandardService继承结构类图如上图:

除了实现Service接口还需要继承Lifecycle

好处:

生命周期统一接口Lifecycle把所有的启动、停止、关闭等都放在一起统一管理

5. Executor线程池与Connector连接器解析初始化

5.1. 配置的线程池及其使用线程

5.2. 使用线程池

5.3. 使用JDK自带的jconsole工具查看线程信息。

5.4. Connector的作用

Connector连接器的作用是监听8080端口,使用request和response处理请求。

5.5. 网络通信

Connector(连接器),监听网络端口、接受网络连接请求、读取请求网络字节流。

根据具体应用层协议(HTTP/AJP)解析字节流,生成统一的Tomcat Request对象

  • 将Tomcat Request对象转成标准的ServletRequest,调用Servlet容器,得到ServletResponse,
  • 将ServletResponse转成 Tomcat Response对象。
  • 将Tomcat Response对象转成网络字节流,将响应字节流写会服务器。

ProtocolHandler由包含了三个部件:Endpoint、Processor、 Adapter

  • Endpoint用来处理底层Socket的网络连接。
  • Processor用于将Endpoint接收到的Socket封装成Request。
  • Adapter充当适配器,用于将Request转换为ServletRequest交给Container进行具体的处理。

5.6. 选择IO模型

5.7. 协议处理器初始化

5.8. 端点初始化

5.9. 绑定IO模型

5.10. 绑定NIO模型

6. Tomcat启动源码解析

6.1. 当所有的组件全部初始化完成后,下面需要让所有的组件启动

6.2. 执行Catalina对象的start方法

6.3. Catalina启动

6.4. StandardServer启动

6.5. StandardService的启动方法

6.6. Connector连接器启动

6.7. 协议处理器启动

6.8. 端点启动

6.9. 具体的IO模型启动

6.10. NioEndpoint的启动方法中,创建线程去接收和处理请求

6.11. 启动Acceptor和Poller线程来接收和处理客户端发送过来的请求

6.12. 使用Acceptor用来接收客户端请求

6.13. 使用Poller线程来处理读写操作

6.14. StandardEngine启动代码

6.15. 启动子容器

6.16. 启动子容器的方法

6.17. 通过JDK本地线程池,启动线程的方式启动子容器。执行AbstractExecutorService类的submit方法

6.18. 执行JDK的线程代码

6.19. StandardHost启动

6.20. StandardContext启动

四、待更新 

;