【Java Web编程 十二】深入理解Tomcat系统架构及工作原理(下)

简介: 【Java Web编程 十二】深入理解Tomcat系统架构及工作原理(下)

Container组件

每个 Service 会包含一个容器。容器由一个引擎可以管理多个虚拟主机。每个虚拟主机可以管理多个 Web 应用。每个 Web 应用会有多个 Servlet 包装器。Engine、Host、Context 和 Wrapper,四个容器之间属于父子关系

四个组件的职责如下:

  • Engine:Servlet 的顶层容器,包含一个或多个Host 子容器
  • Host:虚拟主机,负责 web 应用的部署和 Context 的创建
  • Context:Web 应用上下文,包含多个 Wrapper,负责 web 配置的解析,管理所有的 Web 资源;
  • Wrapper:最底层的容器,是对 Servlet 的封装,负责 Servlet 实例的创建、执行和销毁。

容器的请求处理过程就是在 Engine、Host、Context 和 Wrapper 这四个容器之间层层调用,最后在 Servlet 中执行对应的业务逻辑。各容器都会有一个通道 Pipeline,每个通道上都会有一个 Basic Valve(如StandardEngineValve), 类似一个闸门用来处理 Request 和 Response 。其流程图如下:

请求路径查找流程

了解了两个核心组件后,我们把请求可以简单理解为:连接器的处理流程 + 容器的处理流程 = Tomcat 处理流程。Tomcat 是如何通过请求路径找到对应的虚拟站点?是如何找到对应的 Servlet 呢?我们再来回头看下配置文件里Service的部分:

<!-- Service-->
  <Service name="Catalina">
    <!-- Connector-->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <!-- Container-->
    <Engine name="Catalina" defaultHost="localhost">
      <!-- Realm -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>
      <!-- Host -->
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <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>

通过配置文件分析tomcat处理一个请求的流程,例如我们请求如下地址:http://localhost:8080/docs/list

  1. 连接器监听的端口是8080。由于请求的端口和监听的端口一致,connector接受了该请求,并将转换好的HttpServletRequest传递给容器
  2. 因为引擎的默认虚拟主机是 localhost,并且虚拟主机的目录是webapps。所以请求找到了 tomcat/webapps 目录。
  3. 解析的 docs是 web 程序的应用名,也就是context。此时请求继续从 webapps 目录下找 docs目录。有的时候我们也会把应用名省略。
  4. 解析的 api 是具体的业务逻辑地址。此时需要从 docs/WEB-INF/web.xml 或者通过注解找映射关系,最后调用具体的函数list进行操作。

以下是一个解析示例:

Tomcat生命周期

Tomcat 为了方便管理组件和容器的生命周期,定义了从创建、启动、到停止、销毁共 12 中状态,tomcat 生命周期管理了内部状态变化的规则控制,组件和容器只需实现相应的生命周期方法即可完成各生命周期内的操作(initInternal、startInternal、stopInternal、 destroyInternal)。

比如执行初始化操作时,会判断当前状态是否 New,如果不是则抛出生命周期异常;是则设置当前状态为 Initializing,并执行 initInternal 方法,由子类实现,方法执行成功则设置当 前状态为 Initialized,执行失败则设置为 Failed 状态。整体生命周期状态变化如下图所示:

Tomcat 的生命周期管理引入了事件机制,在组件或容器的生命周期状态发生变化时会通 知事件监听器,监听器通过判断事件的类型来进行相应的操作。事件监听器的添加可以在 server.xml 文件中进行配置,事实上我们从配置中确实可以看的出一些监听器相关配置:

<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

Tomcat 各类容器的配置过程就是通过添加 listener 的方式来进行的,从而达到配置逻辑与容器的解耦。

Tomcat流程管理

Tomcat有两个重要的流程处理,一个是Tomcat的启动流程,一个则是Tomcat的请求处理流程。

Tomcat启动流程

Tomcat的整体启动流程如下,通过如下的方式逐级推动子组件容器的状态变化:

  1. 启动从 Tomcat 提供的 start.sh 脚本开始,shell 脚本会调用 Bootstrap 的 main 方法,实际 调用了 Catalina 相应的 load、start 方法。
  2. load 方法会通过 Digester 进行 config/server.xml 的解析,在解析的过程中会根据 xml 中的关系 和配置信息来创建容器,并设置相关的属性。接着 Catalina 会调用 StandardServer 的 init 、start 方法进行容器的初始化和启动。
  3. StandardServer 完成 init 、 start 方法调用后,会一直监听来自 8005 端口(可配置),如果接收 到 shutdown 命令,则会退出循环监听,执行后续的 stop 和 destroy 方法,完成 Tomcat 容器的 关闭。同时也会调用 JVM 的 Runtime.getRuntime().addShutdownHook 方法,在虚拟机意外退 出的时候来关闭容器。

按照 xml 的配置关系,server 的子元素是 service,service 的子元素是顶层容器 Engine,每层容器有持有自己的子容器,而这些元素都实现了生命周期管理 的各个方法,因此就很容易的完成整个容器的启动、关闭等生命周期的管理。

所有容器都是继承自 ContainerBase,基类中封装了容器中的重复工作,负责启动容器相关的组 件 Loader、Logger、Manager、Cluster、Pipeline,启动子容器(线程池并发启动子容器,通过 线程池 submit 多个线程,调用后返回 Future 对象,线程内部启动子容器,接着调用 Future 对象 的 get 方法来等待执行结果)

Tomcat请求处理流程

上文中我们提到了Tomcat的路径查找方式,请求如何通过配置文件找到对应的处理逻辑,接下来我们完整的来看到一个请求的处理和响应的流程,用一个流程时序图表示如下:

假设来自客户的请求为:http://localhost:8080/Golden/loginServlet

  1. 请求被发送到本机端口 8080,被在那里侦听的 Coyote HTTP/1.1 Connector 获得
  2. Connector 把该请求交给它所在的 Service 的 Engine 来处理,并等待来自 Engine 的回应
  3. Engine 获得请求 localhost:8080/Golden/loginServlet, 匹配到名为 localhost 的 Host(即使匹配不到也把请求交给该 Host 处理,因为该 Host 被定义为该 Engine 的默认主机)
  4. Host 获得请求/Golden/loginServlet,Host 匹配到路径为 /Golden的 Context
  5. Context 获得请求 /loginServlet,匹配它所拥有的Wrapper
  6. Wrapper 构造 HttpServletRequest 对象和 HttpServletResponse 对象,作为参数调用 JspServlet 的 doGetdoPost 方法

返回流程:

  1. Context 把执行完了之后的 HttpServletResponse 对象返回给 Host
  2. Host 把 HttpServletResponse 对象返回给 Engine
  3. Engine 把 Response 对象返回给 Connector
  4. Connector 把 字节流数据返回给客户客户端浏览器。

这样就完成了一次整体的请求和响应。

Tomcat对JSP的处理

我们以上提到的整体处理流程其实是对Servlet的处理流程的概述,实际上大多数场景下我们请求的页面是JSP,而JSP需要被解析为Servlet来执行相关的业务逻辑。

Jasper引擎

客户端访问一个 jsp 文件,最终接收到的响应还是 静态代码,因此 jsp 可以看做是一个运行在服务端的脚本。而 Jasper 的作用就是对 jsp 语法进行解析,生成 servlet 并生成 class 字节码文件,最终将访问的结果直接响应客户端,上文中我们提到的web.xml配置文件中有如下片段,我们可以看到 web.xml 中配置了一个 JspServlet,用于处理所有的 .jsp 或者 .jspx 结尾的请求:

<servlet>
        <servlet-name>jsp</servlet-name>
        <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
        <init-param>
            <param-name>fork</param-name>
            <param-value>false</param-value>
        </init-param>
        <init-param>
            <param-name>xpoweredBy</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>3</load-on-startup>
    </servlet>
    <!-- The mapping for the default servlet -->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    <!-- The mappings for the JSP servlet -->
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>

JSP文件解析流程

我们对JSP的请求在正式转为上述主流程时,前提还有个JSP的解析过程,当我们进行一个jsp请求时,在上述请求主流程的Context容器时,解析之后再继续执行上述主流程的:

最后一步的编译class文件流程详细如下:

总结一下

本篇Blog用较长的篇幅介绍了Tomcat的一些基本原理、组件,以及Tomcat如何处理Servlet请求以及如何解析JSP文件,理解了这些我想对Java Web的全局学习才真正的最终形成了闭环,以后的框架也无非是简化了这流程而已,学习起来应该会更加得心应手。

相关文章
|
11月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
839 1
|
11月前
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
1256 0
|
12月前
|
消息中间件 Java 数据库
Java 基于 DDD 分层架构实战从基础到精通最新实操全流程指南
本文详解基于Java的领域驱动设计(DDD)分层架构实战,结合Spring Boot 3.x、Spring Data JPA 3.x等最新技术栈,通过电商订单系统案例展示如何构建清晰、可维护的微服务架构。内容涵盖项目结构设计、各层实现细节及关键技术点,助力开发者掌握DDD在复杂业务系统中的应用。
1997 0
|
10月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
898 8
|
10月前
|
机器学习/深度学习 自然语言处理 监控
23_Transformer架构详解:从原理到PyTorch实现
Transformer架构自2017年Google发表的论文《Attention Is All You Need》中提出以来,彻底改变了深度学习特别是自然语言处理领域的格局。在短短几年内,Transformer已成为几乎所有现代大型语言模型(LLM)的基础架构,包括BERT、GPT系列、T5等革命性模型。与传统的RNN和LSTM相比,Transformer通过自注意力机制实现了并行化训练,极大提高了模型的训练效率和性能。
2164 0
|
12月前
|
前端开发 Java 数据库
Java 项目实战从入门到精通 :Java Web 在线商城项目开发指南
本文介绍了一个基于Java Web的在线商城项目,涵盖技术方案与应用实例。项目采用Spring、Spring MVC和MyBatis框架,结合MySQL数据库,实现商品展示、购物车、用户注册登录等核心功能。通过Spring Boot快速搭建项目结构,使用JPA进行数据持久化,并通过Thymeleaf模板展示页面。项目结构清晰,适合Java Web初学者学习与拓展。
654 1
|
12月前
|
机器学习/深度学习 人工智能 Java
Java 技术支撑下 AI 与 ML 技术融合的架构设计与落地案例分析
摘要: Java与AI/ML技术的融合为智能化应用提供了强大支持。通过选用Deeplearning4j、DJL等框架解决技术适配问题,并结合Spring生态和JVM优化提升性能。在金融风控、智能制造、医疗影像等领域实现了显著效果,如审批效率提升3倍、设备停机减少41%、医疗诊断延迟降低80%。这种技术融合推动了多行业的智能化升级,展现了广阔的应用前景。
779 0
|
网络协议 Java 应用服务中间件
框架源码私享笔记(01)Tomcat核心架构功能 | 配置详解
本文首先分享了《活出意义来》一书序言中的感悟,强调成功如同幸福,不是刻意追求就能得到,而是全心投入时的副产品。接着探讨了Tomcat的核心功能与架构解析,包括网络连接器(Connector)和Servlet容器(Container),并介绍了其处理HTTP请求的工作流程。文章还详细解释了Tomcat的server.xml配置文件,涵盖了从顶级容器Server到子组件Connector、Engine、Host、Context等的配置参数及作用,帮助读者理解Tomcat的内部机制和配置方法。