Tomcat架构分析—— Engine

news/2025/1/11 12:45:02/

文章目录

  • 一、Tomcat的核心模块(核心组件)
  • 二、Engine 组件
    • 1.核心类与依赖图
    • 2.核心类源码分析
      • 构造函数:
      • 初始化方法 init:
      • 启动方法 start:
    • 3.Engine的启动过程
  • 总结


一、Tomcat的核心模块(核心组件)

Tomcat主要是由这几个模块组成的:

  • Server:
    Server 是最外层的组件,他在Java虚拟集中是单例,主要是用来管理容器下各个Serivce组件的生命周期。

  • Service:
    Server 它将一个或多个连接器(Connector)组件绑定到一个单独的引擎(Engine)上。Service仅仅是一个分组结构,他只是充当架构的一个层面,它并不包含任何其他的功能。

  • Connector:
    连接器,处理与客户端的通信,它负责接收客户请求,以及向客户返回响应结果,每个连接器监控一个指定的IP及端口并通过指定的协议做出响应。可以配置多个连接器使用。

  • Engine:
    引擎,表示一个特定的 Service 的请求处理流水线,从连接器接收和处理所有的请求,将响应返回给适合的连接器,通过连接器传输给用户。在一个服务中(Service)只能有一个引擎。

  • Host:
    虚拟主机,Host 是 Engine 的子容器,一个 Engine 可以包含多个 Host。这个虚拟主机的作用就是运行应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们。

  • Context:
    他代表一个部署的 Web 应用程序本身,它具备了 Servlet 运行的基本环境。一个 Host 可以包含多个Context,每个 Context
    都有一个唯一的路径。Context表示了一个应用的生命周期。

所有客户端的请求都会在连接器(Connector)接受,然后在引擎中(Engine),通过管道(PipeLine),在管道中可以加入各种自定义的阀门(Valve),做拦截,做过滤。所以 Engine 组件,在整次请求中,起着承前启后的关键作用。

下面,我们就着重分析 Engine 组件。

二、Engine 组件

1.核心类与依赖图

Engine 组件的核心类是 StandardEngine

该类的依赖图如下:

从图中可以看到,他也是通过 Lifecycle,来实现他的生命周期管理;并且 Engine 他还是一个容器,实现了Container
在这里插入图片描述

2.核心类源码分析

构造函数:

public StandardEngine() {super();pipeline.setBasic(new StandardEngineValve());/* Set the jmvRoute using the system property jvmRoute */try {setJvmRoute(System.getProperty("jvmRoute"));} catch(Exception ex) {log.warn(sm.getString("standardEngine.jvmRouteFail"));}// By default, the engine will hold the reloading threadbackgroundProcessorDelay = 10;}

可以看出来,主要是给StandardEngine的管道(pipeline)添加了阀门 Value

在 Tomcat 的源码中,以 Value 结尾的类,都是阀门,阀门就是用来提供一些功能的代码,封装成类

打开阀门的核心代码可以看到,他只是拿到 Host,并且调用他通道里面的 Value

final class StandardEngineValve extends ValveBase {//------------------------------------------------------ Constructorpublic StandardEngineValve() {super(true);}@Overridepublic final void invoke(Request request, Response response)throws IOException, ServletException {// Select the Host to be used for this RequestHost host = request.getHost();if (host == null) {// HTTP 0.9 or HTTP 1.0 request without a host when no default host// is defined.// Don't overwrite an existing errorif (!response.isError()) {response.sendError(404);}return;}if (request.isAsyncSupported()) {request.setAsyncSupported(host.getPipeline().isAsyncSupported());}// Ask this Host to process this requesthost.getPipeline().getFirst().invoke(request, response);}
}

初始化方法 init:

既然 Engine 实现了 Lifecycle,那他自然也继承了里面的生命周期方法,在启动时,会触发 initInternal() 方法

源码中看到,在这里初始化以及获取了 Realm

Realm,可以翻译为 " 域 ",在 Tomcat 中,他是一个存储用户名,密码以及和用户名相关联的角色的 " 数据库 "

	@Overrideprotected void initInternal() throws LifecycleException {// Ensure that a Realm is present before any attempt is made to start// one. This will create the default NullRealm if necessary.getRealm();super.initInternal();}

启动方法 start:

上面说到 Engine 实现了 Lifecycle,继承了他的生命周期方法,还有比较核心的 startInternal()

	@Overrideprotected synchronized void startInternal() throws LifecycleException {// Log our server identification informationif (log.isInfoEnabled()) {log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));}// Standard container startupsuper.startInternal();}

他调用了其父类的 startInternal()

	@Overrideprotected synchronized void startInternal() throws LifecycleException {// Start our subordinate components, if anylogger = null;getLogger();Cluster cluster = getClusterInternal();if (cluster instanceof Lifecycle) {((Lifecycle) cluster).start();}Realm realm = getRealmInternal();if (realm instanceof Lifecycle) {((Lifecycle) realm).start();}// Start our child containers, if anyContainer children[] = findChildren();List<Future<Void>> results = new ArrayList<>();for (Container child : children) {results.add(startStopExecutor.submit(new StartChild(child)));}MultiThrowable multiThrowable = null;for (Future<Void> result : results) {try {result.get();} catch (Throwable e) {log.error(sm.getString("containerBase.threadedStartFailed"), e);if (multiThrowable == null) {multiThrowable = new MultiThrowable();}multiThrowable.add(e);}}if (multiThrowable != null) {throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),multiThrowable.getThrowable());}// Start the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();}setState(LifecycleState.STARTING);// Start our threadif (backgroundProcessorDelay > 0) {monitorFuture = Container.getService(ContainerBase.this).getServer().getUtilityExecutor().scheduleWithFixedDelay(new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);}}

这一大段代码中,有几个比较核心的地方:

  • 遍历找到其所有子容器,并启动他们
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (Container child : children) {results.add(startStopExecutor.submit(new StartChild(child)));
}
  • 启动他(Engine )的管道,并执行管道里面的阀门
if (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).start();
}
  • 调用监听事件

在父类的 startInternal() 方法中,用下面这行,调用了 LifecycleBase 里面的方法

setState(LifecycleState.STARTING);

最终定位到,这个方法,调用其注册的监听事件

protected void fireLifecycleEvent(String type, Object data) {LifecycleEvent event = new LifecycleEvent(this, type, data);for (LifecycleListener listener : lifecycleListeners) {listener.lifecycleEvent(event);}}

传进来的,是 EngineConfig ,他实现了 LifecycleListener ,其监听器处理逻辑也很简单,只是记录一下操作日志

3.Engine的启动过程


总结

欢迎指出我的错误!


http://www.ppmy.cn/news/7256.html

相关文章

计算机系统基础实验 - 同符号浮点数加法运算/无符号定点数乘法运算的机器级表示

实验3 同符号浮点数加法运算/无符号定点数乘法运算的机器级表示 实验序号&#xff1a;3 实验名称&#xff1a;同符号浮点数加法运算/无符号定点数乘法运算的机器级表示 适用专业&#xff1a;软件工程 学 时 数&#xff1a;2学时 一、实验目的 1.掌握定点数乘法溢出的判定方法…

JavaScript篇.day05-数组, 基本/引用数据类型函数调用区别

目录 1.Array数组 (1)简介 (2)数组的基本操作 (3)数组的遍历 a. 普通数组的遍历 b.对象数组的遍历 (4)数组对象常用方法 2.基本/引用数据类型函数调用区别 1.Array数组 (1)简介 数组时存放一组数据,长度可变的有序集合索引: 从0开始, 空数组索引无效稀疏数组, 其中含…

青龙面板搭建+QQ机器人

搭建青龙面板首先有个服务器 我这里看到华为云有活动就入手了一个 1.系统选择 centos7.9 华为云购买地址&#xff1a;https://activity.huaweicloud.com/1212_promotion/index.html 2. 服务器上安装宝塔 yum install -y wget && wget -O install.sh http://downl…

大厂与小厂招人的区别,看完多少有点不敢相信

前两天在头条发了一条招人的感慨&#xff0c;关于大厂招人和小公司招人的区别。 大厂&#xff1a;有影响力&#xff0c;有钱&#xff0c;能够吸引了大量的应聘者。因此&#xff0c;也就有了筛选的资格&#xff0c;比如必须985名校毕业&#xff0c;必须35岁以下&#xff0c;不能…

STM32MP157驱动开发——SPI驱动

STM32MP157驱动开发——SPI驱动一、简介1.SPI介绍2.STM32MP1 SPI介绍3. ICM-20608 简介4.Linux下的SPI框架二、驱动开发1&#xff09;IO 的 pinctrl 子节点创建与修改2&#xff09;SPI 设备节点的创建与修改3&#xff09;ICM20608驱动4&#xff09;测试App5&#xff09;运行测试…

Node.js--》三大常见模块的使用讲解

目录 fs文件系统模块 fs.readFile()方法 fs.writeFile()方法 readFile与writeFile的使用 fs模块路径动态拼接问题 path路径模块 path.join()方法 path.basename() path.extname() path.parse() http模块 req请求对象 res响应对象 解决中文乱码问题 响应不同内容…

Spring 为何需要三级缓存解决循环依赖

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java核心知识点、多线程、JVM、常见框架、分布式、微服务、设计模式、架构等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/Tyson0314/Java-learning Gitee地址&#xff1a;https://g…

arthes—线上debug好帮手

arthes简介 以下是arthes官网原文&#xff1a; 通常&#xff0c;本地开发环境无法访问生产环境。如果在生产环境中遇到问题&#xff0c;则无法使用 IDE 远程调试。更糟糕的是&#xff0c;在生产环境中调试是不可接受的&#xff0c;因为它会暂停所有线程&#xff0c;导致服务暂…