tomcat源码02 - 理解Tomcat架构设计

发布于:2025-07-13 ⋅ 阅读:(19) ⋅ 点赞:(0)

理解Tomcat架构设计

一:准备知识

1:tomcat & catalina

Tomcat的前身为Catalina,Catalina又是一个轻量级的Servlet容器

在美国,catalina是一个很美的小岛。所以Tomcat作者的寓意可能是想把Tomcat设计成一个优雅美丽且轻量级的web服务器

tomcat从4.x版本开始除了作为支持Servlet的容器外,额外加入了很多的功能,比如:jsp、el、naming等等

所以说Tomcat不仅仅是Catalina

所以:Tomcat是⼀个由⼀系列可配置(conf/server.xml)的组件构成的Web容器,⽽Catalina是Tomcat的servlet容器

从另⼀个⻆度来说,Tomcat 本质上就是⼀款 Servlet 容器

因为 Catalina 才是 Tomcat 的核⼼,其他模块都是为Catalina 提供⽀撑的。

2:什么是Servlet

所谓Servlet,其实就是Sun为了让Java能实现动态可交互的网页,从而进入Web编程领域而制定的一套标准!

[Servlet是web标准]

在互联网兴起之初,当时的Sun公司(后面被Oracle收购)已然看到了这次机遇,于是设计出了Applet来对Web应用的支持

不过事实却并不是预期那么得好,Sun悲催地发现Applet并没有给业界带来多大的影响

经过反思,Sun就想既然机遇出现了,市场前景也非常不错,总不能白白放弃了呀,怎么办呢?于是Servlet规范诞生了

一个Servlet主要做下面三件事情:

  • 创建并填充Request对象,包括:URI、参数、method、请求头信息、请求体信息等
  • 创建Response对象
  • 执行业务逻辑,将结果通过Response的输出流输出到客户端

Servlet没有main方法,所以,如果要执行,则需要在一个容器里面才能执行,这个容器就是为了支持Servlet的功能而存在

Tomcat其实就是一个Servlet容器的实现

二:不同角度看Tomcat架构

目前找到的tomcat最全架构图

在这里插入图片描述

1:从组件的角度看

1.1:Server组件 - 服务器

它提供了一种优雅的方式来启动和停止整个系统,不必单独启停连接器和容器

它是Tomcat构成的顶级构成元素,所有一切均包含在Server中

1.2:Service组件 - 服务

Server可以运行多个服务。

比如一个Tomcat里面可运行订单服务、支付服务、用户服务等等;

Server的实现类StandardServer可以包含一个到多个Services, Service的实现类为StandardService调用了容器(Container)接口,其实是调用了Servlet Engine(引擎),而且StandardService类中也指明了该Service归属的Server;

Service内部还有很多支撑组件:

  • Manager – 管理器,用于管理会话Session
  • Logger – 日志器,用于管理日志
  • Loader – 加载器,和类加载有关,只会开放给Context所使用
  • Pipeline – 管道组件,配合Valve实现过滤器功能
  • Valve – 阀门组件,配合Pipeline实现过滤器功能
  • Realm – 认证授权组件
1.3:container组件 - 容器

可以看做Servlet容器;引擎(Engine)、主机(Host)、上下文(Context)和Wraper均继承自Container接口,所以它们都是容器。

在这里插入图片描述

1.4:connector组件 - 连接器

将Service服务和container容器进行连接

首先它需要注册到一个Service,它的作用就是把来自客户端的请求转发到Container(容器),它支持AJP, Http, Https协议

在这里插入图片描述

2:从web.xml配置和模块对应角度

上述模块的理解不是孤立的,它直接映射为Tomcat的web.xml配置

<?xml version="1.0" encoding="UTF-8" ?>
<Server port="8080" shutdown="SHUTDOWN">
    <!-- 配置Jasper监听器,用于支持JSP的编译 -->
    <Listener className="org.apache.catalina.core.JasperListener" />
    <!-- 配置JRE内存泄露预防监听器,用于检测和预防Java应用中的内存泄露 -->
    <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
    <!-- 配置线程本地泄露预防监听器,用于检测和预防Tomcat中可能出现的内存泄露问题 -->
    <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

    <!-- 定义全局命名资源,这里配置了用户数据库资源 -->
    <GlobalNamingResources>
        <!-- 用户数据库资源,认证方式为Container,类型为org.apache.catalina.UserDatabase,描述为可更新和保存的用户数据库,工厂为MemoryUserDatabaseFactory,路径为conf/tomcat-users.xml -->
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
    </GlobalNamingResources>

    <!-- 配置Catalina服务 -->
    <Service name="Catalina">
        <!-- 配置HTTP连接器,端口为8080,协议为HTTP/1.1,地址为127.0.0.1,连接超时时间为20000毫秒,SSL端口为8443 -->
        <Connector port="8080" protocol="HTTP/1.1" address="127.0.0.1" connectionTimeout="20000" redirectPort="8443" />
        <!-- 配置引擎,名称为Catalina,默认主机为例.com -->
        <Engine name="Catalina" defaultHost="example.com">
            <!-- 配置用户数据库领域的认证器,资源名称为UserDatabase -->
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
            <!-- 配置主机,名称为例.com,应用基础目录为webapps,自动解压WAR文件,自动部署应用 -->
            <Host name="example.com" appBase="webapps" unpackWARs="true" autoDeploy="true">
                <!-- 配置访问日志阀门,日志目录为logs,文件前缀为localhost_access_log.,文件后缀为.txt,日志模式为常见的Apache日志格式 -->
                <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="%h %l %u %t &quot;%r&quot; %s %b" />
            </Host>
        </Engine>
    </Service>
</Server>

可以发现web.xml的Node结构和组件是对应的。

3:从一个完整请求角度来看

假设来自客户的请求为:http://localhost:8080/test/index.jsp 请求被发送到本机端口8080

被在那里侦听的Coyote HTTP/1.1 Connector发现,会发生如下步骤

在这里插入图片描述

  1. Connector把该请求交给它所在的Service的Engine来处理,并等待Engine的回应
  2. Engine获得请求localhost:8080/test/index.jsp,匹配它所有虚拟主机Host
  3. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
  4. localhost Host获得请求/test/index.jsp,匹配它所拥有的所有Context
  5. Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
  6. path="/test"的Context获得请求/index.jsp,在它的mapping table中寻找对应的servlet
  7. Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类,构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
  8. Context把执行完了之后的HttpServletResponse对象返回给Host
  9. Host把HttpServletResponse对象返回给Engine
  10. Engine把HttpServletResponse对象返回给Connector
  11. Connector把HttpServletResponse对象返回给客户browser

4:从源码的设计角度看

在这里插入图片描述

从功能的角度将Tomcat源代码分成5个子模块,分别是:

4.1:Jsper模块

这个子模块负责jsp页面的解析、jsp属性的验证,同时也负责将jsp页面动态转换为java代码并编译成class文件。

在Tomcat源代码中,凡是属于org.apache.jasper包及其子包中的源代码都属于这个子模块;

4.2:Servlet和Jsp模块

这个子模块的源代码属于javax.servlet包及其子包

如我们非常熟悉的javax.servlet.Servlet接口、javax.servet.http.HttpServlet类就位于这个子模块

4.3:Catalina模块

这个子模块包含了所有以org.apache.catalina开头的java源代码。

该子模块的任务是规范了Tomcat的总体架构,定义了Server、Service、Host、Connector、Context、Session及Cluster等关键组件及这些组件的实现

这个子模块大量运用了Composite设计模式。同时也规范了Catalina的启动及停止等事件的执行流程。

4.4:Connector模块

如果说上面三个子模块实现了Tomcat应用服务器的话,那么这个子模块就是Web服务器的实现。

所谓连接器(Connector)就是一个连接客户和应用服务器的桥梁

它接收用户的请求,并把用户请求包装成标准的Http请求(包含协议名称,请求头Head,请求方法是Get还是Post等等)。

同时,这个子模块还按照标准的Http协议,负责给客户端发送响应页面

比如在请求页面未发现时,connector就会给客户端浏览器发送标准的Http 404错误响应页面。

4.5:Resource模块

这个子模块包含一些资源文件,如Server.xml及Web.xml配置文件

严格说来,这个子模块不包含java源代码,但是它还是Tomcat编译运行所必需的


网站公告

今日签到

点亮在社区的每一天
去签到