Tomcat架构分析—— Engine

news2025/1/12 18:16:07

文章目录

  • 一、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 thread
        backgroundProcessorDelay = 10;

}

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

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

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

final class StandardEngineValve extends ValveBase {
    //------------------------------------------------------ Constructor
    public StandardEngineValve() {
        super(true);
    }
	@Override
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Select the Host to be used for this Request
        Host 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 error
            if (!response.isError()) {
                response.sendError(404);
            }
            return;
        }
        if (request.isAsyncSupported()) {
            request.setAsyncSupported(host.getPipeline().isAsyncSupported());
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);
    }
}

初始化方法 init:

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

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

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

	@Override
    protected 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()

	@Override
    protected synchronized void startInternal() throws LifecycleException {

        // Log our server identification information
        if (log.isInfoEnabled()) {
            log.info(sm.getString("standardEngine.start", ServerInfo.getServerInfo()));
        }

        // Standard container startup
        super.startInternal();
    }

他调用了其父类的 startInternal()

	@Override
    protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = 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 any
        Container 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 any
        if (pipeline instanceof Lifecycle) {
            ((Lifecycle) pipeline).start();
        }

        setState(LifecycleState.STARTING);

        // Start our thread
        if (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.coloradmin.cn/o/117967.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习之支持向量机(手推公式版)

文章目录前言1. 间隔与支持向量2. 函数方程描述3. 参数求解3.1 拉格朗日乘数3.2 拉格朗日对偶函数前言 支持向量机(Support(Support(Support VectorVectorVector Machine,SVM)Machine,SVM)Machine,SVM)源于统计学习理论&#xff0c;是一种二分类模型&#xff0c;是机器学习中获…

mysql查询当天,近一周,近一个月,近一年的数据

1.mysql查询当天的数据 select * from table where to_days(时间字段) to_days(now()); 2.mysql查询昨天的数据 select * from table where to_days(now( ) ) - to_days( 时间字段名) < 1 3.mysql查询近一周的数据 SELECT * FROM table WHERE date(时间字段) > D…

MySQL表的创建修改删除

目录 1、表的创建 2、查看表结构 3、表的修改 4、表的删除 1、表的创建 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎&#xff1b;说明&#xff1a; field 表示列名 datatype 表…

计算机系统基础实验 - 定点数加减法的机器级表示

实验序号&#xff1a;2 实验名称&#xff1a;定点数加减法的机器级表示 适用专业&#xff1a;软件工程 学 时 数&#xff1a;2学时 一、实验目的 1、掌握定点数加法的机器级表示。 2、掌握定点数减法的机器级表示。 3、掌握EFLAGS中4个牵涉到计算的标志位的计算方法。 4、掌握…

python实现动态柱状图

目录 一.基础柱状图 反转x轴&#xff0c;y轴&#xff0c;设置数值标签在右侧 小结 二.基础时间线柱状图 三.GDP动态柱状图绘制 1.了解列表的sort方法并配合lambda匿名函数完成列表排序 2.完成图表所需数据 3.完成GDP动态图表绘制 添加主题类型 设置动态标题 四.完整代码…

5.6 try语句块和异常处理

文章目录throw表达式(异常检测)try语句块&#xff08;异常处理&#xff09;编写处理代码函数在寻找处理代码的过程中退出标准异常异常是指存在于运行时的反常行为&#xff0c;这些行为超出了函数正常功能的范围。典型的异常包括失去数据库连接以及遇到意外输入等。当程序的某部…

Android Studio实现一个旅游课题手机app

文章目录&#xff1a; 目录 一、课题介绍 二、软件的运行环境 三、软件运行截图 四、软件项目总结 一、课题介绍 本次课题是实现了一个外出旅游的app&#xff0c;通过app可以显示景点的信息&#xff0c;以及根据地区查询&#xff0c;具体功能如下&#xff1a; 客户端 1.用…

【算法】面试题 - 数组(附讲解视频)

目录标题原地修改数组&#xff08;快慢指针&#xff09;26. 删除有序数组中的重复项扩展&#xff1a;83. 删除排序链表中的重复元素27. 移除元素283. 移动零左右指针167. 两数之和15. 三数之和[一个方法团灭 NSUM 问题](https://blog.csdn.net/yzx3105/article/details/1284606…

JavaWeb学生系统+教师系统+管理员系统

目录&#xff1a;一、前言&#xff1a;一、用到的技术&#xff1a;1.前端&#xff1a;HTMLCssJavaScriptAjaxJQueryBootStrap2.后端&#xff1a;ServletJSPSpringMVCJPA二、系统实现的效果&#xff1a;1.登录登出功能&#xff1a;(1)不同用户可以跳转到不同的系统页面。(2)设有…

window 和虚拟机ubuntu通讯的网络设置 本地连接桥接和NAT

工作需要&#xff0c;最近在linux下开发&#xff0c;需要将windows里的文件传至虚拟机里以及下位机树莓派中&#xff0c;三者需要实现互传。 windows连接树莓派时是采用网口建立本地连接的&#xff0c;而当不需要连接树莓派时&#xff0c;windows和虚拟机不能通过有线本地连接…

09、SpringCloud 系列:Nacos - 配置文件中心

SpringCloud 系列列表&#xff1a; 文章名文章地址01、Eureka - 集群、服务发现https://blog.csdn.net/qq_46023503/article/details/12831902302、Ribbon - 负载均衡https://blog.csdn.net/qq_46023503/article/details/12833228803、OpenFeign - 远程调用https://blog.csdn.…

Python接口测试实战1(下)- 接口测试工具的使用

本节内容 抓包工具的使用Postman的使用 抓包工具的使用 抓包工具简介 Chrome/Firefox 开发者工具: 浏览器内置&#xff0c;方便易用Fiddler/Charles: 基于代理的抓包&#xff0c;功能强大&#xff0c;可以手机抓包&#xff0c;模拟弱网&#xff0c;拦截请求&#xff0c;定制…

xpdf在windows下的编译记录

目录 1、下载源码 ​编辑 2、准备工作 3、编译freetype 3.1 打开vs工程 3.2 生成之后查看 4、编译zlib 5、编译libpng 6、编译lcms 7、编译xpdf 8、存在问题 1、下载源码 Xpdf官网下载&#xff1a;Download Xpdf and XpdfReader 2、准备工作 3、编译freetype 3.1 打…

财务数字化转型怎么转?从哪几个方面出发

财务的数字化转型如何进行&#xff1f;许多企业在推动各大业务部门进行数字化转型时&#xff0c;往往会忽略财务部门。然而&#xff0c;作为掌握公司核心资源与数据和推动企业数字化建设的部门&#xff0c;财务也应成为企业数字化转型的重要突破口。 这篇就用几个案例详细拆解…

React 入门:实战案例 Github搜索_静态组件

文章目录开发前的准备实现组件静态代码开发前的准备 目标实现页面效果, 及组件拆分&#xff0c;如下图所示 组件代码结构规划 外部资源准备 引入第三方样式 bootstrap CSS 样式库 bootstrap 官网有 v3、v4、v5 三个版本&#xff0c;本文使用的 v3 版本的样式&#xff0c;可…

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

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

一文搞懂中台与产品微服务、SaaS的区别

出处&#xff1a;本文摘录自《中台产品经理》一书 谈到“中台”&#xff0c;我们不得不说的另外两个概念就是“微服务”与“SaaS”&#xff0c;有很多人会把“中台”与这两个概念画上等号。但实际上&#xff0c;中台 ≠产品微服务 ≠ SaaS。这两个概念看似与中台很相似&#xf…

因斯布鲁克大学发明一种更快的新型量子计算机

奇偶校验计算机可以在单个量子比特上执行两个或多个量子比特之间的操作&#xff0c;更容易实现复杂的算法。 该团队由 Wolfgang Lechner&#xff08;右一&#xff09;领导&#xff0c;包括Kilian Ender&#xff08;右二&#xff09;、Anette Messinger&#xff08;左二&#xf…

七天免登录(Cookie+session)+ 页面显示动画人物效果(萌娘+气泡效果)

一.七天免登录&#xff08;Cookiesession&#xff09;&#xff0c;基于实现jsp页面 页面效果&#xff1a;勾选7天免登录复选框&#xff0c;输入用户名和密码&#xff0c;点击登录即可&#xff0c;登录后可在cookie中查看用户登录信息&#xff0c;以及用户过期时间 1.登录页面&…

通过heartbeat实现mysql高可用

192.168.6.128 主/heartbeat1 192.168.6.129 从/heartbeat2 192.168.6.131 漂移地址 主备基础&#xff1a;需要在128和129服务器上&#xff0c;搭建mysql主从复制 环境基础配置 128、129操作 # sed -i "s/SELINUXenforcing/SELINUXdisabled/g" /etc/selinux/…