Tomcat源码解析(四):StandardServer和StandardService

news2024/11/16 9:25:38

Tomcat源码系列文章

Tomcat源码解析(一):Tomcat整体架构

Tomcat源码解析(二):Bootstrap和Catalina

Tomcat源码解析(三):LifeCycle生命周期管理

Tomcat源码解析(四):StandardServer和StandardService


文章目录

  • 前言
  • 一、StandardServer
    • 1、StandardServer实例化
      • 1.1、Server接口
      • 1.2、解析server.xml
      • 1.3、解析\<Server>标签
    • 2、init初始化
      • 2.1、LifecycleBase#init
      • 2.2、LifecycleMBeanBase#initInternal
      • 3.2、StandServer#initInternal
    • 3、start启动
      • 3.1、LifecycleBase#start
      • 3.2、StandardServer#start
    • 4、stop停止
    • 5、destroy销毁
  • 二、StandardService
    • 1、StandardService实例化
      • 1.1、Service接口
      • 1.2、解析server.xml
      • 1.3、解析\<Service>标签
    • 2、init初始化
      • 2.1、StandardService#initInternal
    • 3、start、stop、destroy
  • 总结


前言

  前文Tomcat源码解析(二):Bootstrap和Catalina介绍Tomcat的启动类的加载,在Catalina初始化时加载了server.xml,创建ServerServiceConnector等一些列组件,然后调用Serverinitstart方法,启动tomcat。Tomcat源码解析(三):LifeCycle生命周期管理介绍了组件init、start、stop等共同生命周期方法,使用模板方法设计模式,具体的实现类由子类去实现。

在这里插入图片描述


一、StandardServer

1、StandardServer实例化

  • 一个Server类的实例就代表了一个Tomcat的容器,一个Tomcat进程只会有一个Server实例

1.1、Server接口

  Server(实现类StandardServer)类图如下,只需要关注左边部分即可。

在这里插入图片描述

  右边部分为jmx内容,tomcat允许我们使用jmx对tomcat进行监控、管理,截图如下。以后有机会出文章单独讲,所以此篇文章涉及Jmx接口内容就不细讲了。

在这里插入图片描述

1.2、解析server.xml

  • 通过解析server.xml实例化StandardServer,并设置server.xml文件中定义的属性初始化
  <!-- 1.属性说明
    port:指定一个端口,这个端口负责监听关闭Tomcat的请求
    shutdown:向以上端口发送的关闭服务器的命令字符串
  -->
<Server port="8005" shutdown="SHUTDOWN">

  <!-- 2.Listener 相关 -->
  <Listener className="org.apache.catalina.core.AprLifecycleListener" />
  <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener"/>

  <!-- 3.GlobalNamingResources 相关 -->
  <GlobalNamingResources>
    <Environment name="simpleValue" type="java.lang.Integer" value="30"/>
    <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>

  <!-- 4.service 相关 -->
  <Service name="Catalina">
  		...
  </Service>
</Server>

1.3、解析<Server>标签

  • <Server>标签内容用来实例化StandardServer组件
# Catalina#createStartDigester方法
/** 解析<server>标签实例化StandardServer对象,并push到操作栈中 **/
digester.addObjectCreate("Server",
                         "org.apache.catalina.core.StandardServer",
                         "className");
                         
/** 解析<server>标签将标签中属性值映射到StandardServer对象中**/  
digester.addSetProperties("Server");

/** 解析</server>标签将操作栈栈顶对象设置到次栈顶对象属性中**/
//将StandardServer对象设置到Catalina启动类对象的server属性中
digester.addSetNext("Server",
                    "setServer",
                    "org.apache.catalina.Server"
  • 将<server>标签属性映射到StandardServer对象属性中
/**
 * Tomcat shutdown操作,对应字符串指令
 */
private String shutdown = "SHUTDOWN";

/**
 * Tomcat ShutDown操作,服务端监听Socket端口号。
 */
private int port = 8005;

/**
 * Tomcat ShutDown执行,服务端监听Socket地址。
 */
private String address = "localhost";

在这里插入图片描述

  • 监听SHUTDOWN命令Socket服务在前面Tomcat源码解析(二):Bootstrap和Catalina文章里面有讲

2、init初始化

初始化方法是在Catlina的load方法中加载server.xml后,调用getServer().init()触发的

2.1、LifecycleBase#init

  • StandardServer的init方法由父类LifecycleBase实现
  • LifecycleBase使用模板模式将初始化的具体操作留给了每个容器自己实现
@Override
public final synchronized void init() throws LifecycleException {
    // 非NEW状态,不允许调用init()方法
    if (!state.equals(LifecycleState.NEW)) {
    	// 抛出异常
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }
 
    try {
        // 初始化逻辑之前,先将状态变更为`INITIALIZING(初始化过程中状态)`
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        // 初始化,该方法为一个abstract方法,需要组件自行实现
        initInternal();
        // 初始化完成之后,状态变更为`INITIALIZED(初始化完成状态)`
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        // 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED(异常状态)`
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
}
// 真正的初始化方法,需要子类实现此方法
protected abstract void initInternal() throws LifecycleException;

2.2、LifecycleMBeanBase#initInternal

  • LifecycleBase的父类LifecycleMBeanBase可以看到initInternal方法的实现
  • 但这些都是jmx的内容,跳过,再往上找父类也就是StandardServer了
@Override
protected void initInternal() throws LifecycleException {
    if (oname == null) {
        mserver = Registry.getRegistry(null, null).getMBeanServer();
        oname = register(this, getObjectNameKeyProperties());
    }
}

3.2、StandServer#initInternal

  • 核心内容就是最后一行内容,初始化当前Server下的所有Services
  • Server里的service是在server.xml里定义的,在Catalina解析server.xml的时候初始化,并注入到Server对象里
  • JNDI:就是通过配置一些xml文件,方便用户直接调用API使用某些通用的资源(不常用,不做过多介绍了)
@Override
protected void initInternal() throws LifecycleException {
    // 调用父类LifecycleMBeanBase中的实现
    super.initInternal();

    // JNDI服务初始化
    globalNamingResources.init();
    
	// 省略jmx注册对象内容
	...
	
    // 初始化Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

3、start启动

启动方法是在Catlina的start方法中调用getServer().start()触发的

3.1、LifecycleBase#start

  • 和init方法一样,StandardServer的start方法也是父类LifecycleBase实现,具体实现留给子类
  • 和init方法不一样,LifecycleBase的父类LifecycleMBeanBase没有startInternal方法的实现
public final synchronized void start() throws LifecycleException {
    // `STARTING_PREP启动前`、`STARTING启动中`和`STARTED启动完成时,将忽略start()逻辑
    if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
            LifecycleState.STARTED.equals(state)) {
        return;
    }
 
    // `NEW`状态时,执行init()方法
    if (state.equals(LifecycleState.NEW)) {
        init();
    }
 
    // `FAILED`状态时,执行stop()方法
    else if (state.equals(LifecycleState.FAILED)) {
        stop();
    }
 
    // 不是`INITIALIZED初始化完成`和`STOPPED停止完成`时,则说明是非法的操作
    else if (!state.equals(LifecycleState.INITIALIZED) &&
            !state.equals(LifecycleState.STOPPED)) {
        // 抛出异常
        invalidTransition(Lifecycle.BEFORE_START_EVENT);
    }
 
 	// 一般流程会走到这里,刚走完初始化流程,状态为INITIALIZED(初始化完成状态)
    try {
        // start前的状态设置
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        // start逻辑,抽象方法,由组件自行实现
        startInternal();
        // start过程中,可能因为某些原因失败,这时需要stop操作
        if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
        	// 抛出异常
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            // 设置状态为STARTED(启动完成状态)
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        // 异常状态
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
    }
}

// 真正的启动方法,需要子类实现此方法
protected abstract void startInternal() throws LifecycleException;

3.2、StandardServer#start

  • 更正当前组件状态为STARTING(启动过程中状态)
  • 核心内容启动所有service组件
@Override
protected void startInternal() throws LifecycleException {
    // 通知监听器当前组件触发 CONFIGURE_START_EVENT事件
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    
    // 更正当前组件状态为STARTING(启动过程中状态)
    setState(LifecycleState.STARTING);
    
    // 启动JNDI服务
    globalNamingResources.start();
    
    // 启动所有service组件
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
}

4、stop停止

停止方法是在Catlina的stop方法中调用getServer().stop()触发的

  • StandardServer的stop方法也是父类LifecycleBase实现,具体实现留给子类
  • LifecycleBase里面无非就是状态切换,直接进入StandardServer#stopInternal
  • 核心内容关闭所有service组件
@Override
protected void stopInternal() throws LifecycleException {
    // 通知监听器当前组件触发 CONFIGURE_STOP_EVENT事件
    fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
    
    // 更正当前组件状态为STOPPING
    setState(LifecycleState.STOPPING);
    
    // 关闭所有service组件
    for (int i = 0; i < services.length; i++) {
        services[i].stop();
    }

    // 关闭JNDI服务
    globalNamingResources.stop();

    // 停止监听 shutdown命令 Socket服务
    stopAwait();
}
  • 设置stopAwait标识为true,跳出阻塞SHUTDOWN的socket服务
  • 设置awaitSocket为null,关闭Socket服务,不在监听SHUTDOWN命令
public void stopAwait() {
    stopAwait=true;
    Thread t = awaitThread;
    if (t != null) {
        ServerSocket s = awaitSocket;
        if (s != null) {
            awaitSocket = null;
            try {
                s.close();
            } catch (IOException e) {
                // Ignored
            }
        }
        t.interrupt();
        try {
            t.join(1000);
        } catch (InterruptedException e) {
            // Ignored
        }
    }
}

在这里插入图片描述

5、destroy销毁

销毁方法是在Catlina的stop方法中调用getServer().destroy()触发的

  • StandardServer的destroy方法也是父类LifecycleBase实现,具体实现留给子类
  • 核心内容调用所有Service组件destroy方法
@Override
protected void destroyInternal() throws LifecycleException {
    // 调用所有Service子组件启动方法destroy 
    for (int i = 0; i < services.length; i++) {
        services[i].destroy();
    }
    // 销毁JND全局资源
    globalNamingResources.destroy();

	// 省略jmx内容
	...
}

二、StandardService

1、StandardService实例化

1.1、Service接口

  Service(实现类StandardService)类图如下,只需要关注左边部分即可,右边部分为jmx内容。

在这里插入图片描述

1.2、解析server.xml

  • 大概可以分为4个部分,service属性、executor属性、connector属性、engine属性
  • 在Tomcat源码解析(一):Tomcat整体架构讲过,一个Server实例可以包含多个Service对象(一个容器和多个连接器组合)
    • 一个容器就是Engine表示顶级容器,包含Host、Context(web应用)、Wrapper(Servlet)
    • 多个连接器就是Connector,同个容器可以支持不同协议和端口的部署访问
<!--
    每个Service元素只能有一个Engine元素
    元素处理在同一个<Service>中所有<Connector>元素接收到的客户请求
-->

<!-- 1. 属性说明
	name:Service的名称
-->
<Service name="Catalina">
 
    <!--2. 一个或多个excecutors -->
    <!--
    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
        maxThreads="150" minSpareThreads="4"/>
    -->
 
    <!--
		3.Connector元素:
		由Connector接口定义.<Connector>元素代表与客户程序实际交互的组件
		它负责接收客户请求,以及向客户返回响应结果.
    -->
    <Connector port="8080" protocol="HTTP/1.1" 
    	connectionTimeout="20000" redirectPort="8443" />
    <!-- 负责和其他HTTP服务器建立连接
    在把Tomcat与其他HTTP服务器集成时就需要用到这个连接器。 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
	
    <!--
		4. Engine
    -->
    <Engine name="Catalina" defaultHost="localhost">
    
    </Engine>
</Service>

1.3、解析<Service>标签

  • 标签内容用来实例化StandardServer组件
  • addService将创建的StandardService对象设置到Server对象下的services集合
# Catalina#createStartDigester方法
/** 解析<Service>标签实例化StandardService对象 **/
digester.addObjectCreate("Server/Service",
                         "org.apache.catalina.core.StandardService",
                         "className");
                         
/** 解析<Service>标签将标签中属性值映射到StandardService对象中 **/       
digester.addSetProperties("Server/Service");

/** 将StandardService对象设置到Server对象的service集合中 **/   
digester.addSetNext("Server/Service",
                    "addService",
                    "org.apache.catalina.Service");

2、init初始化

初始化方法是在Server的initInternal方法循环遍历调用services[i].init()触发的

2.1、StandardService#initInternal

  • StandardService和StandardServer一样也是继承自LifecycleMBeanBase
  • 初始化具体实现留给子类也就是StandardService#initInternal
  • 顶级容器engine、请求url映射Mapper、执行器Executor、连接器Connector初始化内容后面章节单独讲
@Override
protected void initInternal() throws LifecycleException {
	// 父类LifecycleMBeanBase注册jmx相关,跳过
    super.initInternal();
	
	// 顶级容器engine初始化
    if (engine != null) {
        engine.init();
    }

    // 执行器Executor初始化
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // 初始化mapperListener,只是注册JMX,跳过
    mapperListener.init();

    // 连接器Connector初始化
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
                connector.init();
            } catch (Exception e) {
	...
            }
        }
    }
}

3、start、stop、destroy

  start、stop、destroy的实现内容与initInternal类似,内部都是调用顶级容器engine、请求url映射Mapper、执行器Executor、连接器Connector的start、stop、destroy方法,后面章节单独讲


总结

  • 一个Tomcat进程只有一个Server实例,一个Server实例可以包含多个Service对象
  • Server组件通过调用initstart方法来启动tomcat
  • 而Server的init方法和start方法则是调用多个Serviceinitstart方法
  • 而一个Service的init方法和start方法则是调用一个顶级容器engine、一个请求url映射Mapper、多个执行器Executor、多个连接器Connectorinitstart方法

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1507297.html

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

相关文章

大数据赋能,能源企业的智慧转型之路

在数字洪流中&#xff0c;大数据已经成为推动产业升级的新引擎。特别是在能源行业&#xff0c;大数据的应用正引领着一场深刻的智慧转型。今天&#xff0c;我们就来探讨大数据如何在能源企业中发挥其独特的魅力&#xff0c;助力企业提效降本&#xff0c;实现绿色发展。 动态监控…

R语言读取大型NetCDF文件

失踪人口回归&#xff0c;本篇来介绍下R语言读取大型NetCDF文件的一些实践。 1 NetCDF数据简介 先给一段Wiki上关于NetCDF的定义。 NetCDF (Network Common Data Form) is a set of software libraries and self-describing, machine-independent data formats that support…

光线追踪11 - Positionable Camera(可定位相机)

相机和介质一样&#xff0c;调试起来很麻烦&#xff0c;所以我总是逐步开发我的相机。首先&#xff0c;我们允许可调节的视野&#xff08;fov&#xff09;。这是渲染图像从一边到另一边的视觉角度。由于我们的图像不是正方形的&#xff0c;水平和垂直的视野是不同的。我总是使用…

mybatis基础操作(三)

动态sql 通过动态sql实现多条件查询&#xff0c;这里以查询为例&#xff0c;实现动态sql的书写。 创建members表 创建表并插入数据&#xff1a; create table members (member_id int (11),member_nick varchar (60),member_gender char (15),member_age int (11),member_c…

【探索程序员职业赛道:挑战与机遇】

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

微信小程序(五十三)修改用户头像与昵称

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.外界面个人资料基本模块 2.资料修改界面同步问题实现&#xff08;细节挺多&#xff0c;考虑了后期转服务器端的方便之处&#xff09; 源码&#xff1a; app.json {"window": {},"usingCompone…

什么是5G边缘计算网关?

随着5G技术的飞速发展和普及&#xff0c;边缘计算作为5G时代的关键技术之一&#xff0c;正日益受到业界的关注。而5G边缘计算网关&#xff0c;作为连接5G网络和边缘计算节点的桥梁&#xff0c;扮演着至关重要的角色。HiWoo Box&#xff0c;作为一款卓越的5G边缘计算网关&#x…

【Spring云原生系列】SpringBoot+Spring Cloud Stream:消息驱动架构(MDA)解析,实现异步处理与解耦合

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

使用Spring的AOP

使用Spring的AOP 一、AOP 的常用注解1.切面类Aspect2.Pointcut3.前置通知Before4.后置通知AfterReturning5.环绕通知Around6.异常通知AfterThrowing7.最终通知After8.切面顺序Order9.启用自动代理EnableAspectJAutoProxy 二、AOP注解方式开发三、AOP 全注解开发四、基于XML配置…

RDD算子介绍(二)

1. coalesce 用于缩减分区&#xff0c;减少分区个数&#xff0c;减少任务调度成本。 val rdd : RDD[Int] sc.makeRDD(List(1, 2, 3, 4), 4) val newRDD rdd.coalesce(2) newRDD.saveAsTextFile("output") 分区数可以减少&#xff0c;但是减少后的分区里的数据分布…

Keepalived+LVS构建高可用集群

目录 一、Keepalive基础介绍 1. Keepalive与VRRP 2. VRRP相关技术 3. 工作原理 4. 模块 5. 架构 6. 安装 7. Keepalived 相关文件 7.1 配置组成 7.2 全局配置 7.3 VRRP实例配置&#xff08;lvs调度器&#xff09; 7.4 虚拟服务器与真实服务器配置 二、Keepalived…

【数据库】软件测试之MySQL数据库面试总结

有表如下&#xff1a; Student 学生表 SC 成绩表 Course 课程表 Teacher 老师表 每个学生可以学习多门课程&#xff0c;每一个课程都有得分&#xff0c;每一门课程都有老师来教&#xff0c;一个老师可以教多个学生 1、查询姓‘朱’的学生名单 select * from Student whe…

010Editor汉化版+下载+注册码+模板bug

项目场景&#xff1a; 这天我想使用我的不知名的一个破解版本的010Edit来查看一个EXE程序&#xff0c;并想使用模板功能&#xff0c;但是发现没有该模板还无法下载最新模板 问题描述 010Edit联网后需要注册码&#xff1a; 010 Editor 激活码生成器 使用方法 参照教程使用0…

面试题:分布式锁用了 Redis 的什么数据结构

在使用 Redis 实现分布式锁时&#xff0c;通常使用 Redis 的字符串&#xff08;String&#xff09;。Redis 的字符串是最基本的数据类型&#xff0c;一个键对应一个值&#xff0c;它能够存储任何形式的字符串&#xff0c;包括二进制数据。字符串类型的值最多可以是 512MB。 Re…

浅谈Maven

Maven能为我们解决什么问题 1&#xff1a;添加第三方jar包 按照最原始的做法&#xff0c;我们是手动复制jar包到项目WEB-INF/lib下&#xff0c;每个项目都会有一份&#xff0c;造成大量重复文件。而Maven将jar包放在本地仓库中统一管理&#xff0c;需要jar包只需要用坐标的方式…

使用Canvas绘制一个自适应长度的折线图

要求x轴根据数据长度自适应 y轴根据数据最大值取长度值 <template><div ref"cvsContainer" class"cvs-container"><canvas ref"cvs" class"canvas"></canvas></div> </template><script set…

188基于matlab的AR模型参数估计

基于matlab的AR模型参数估计&#xff0c;burg法和ule-Walker法估计信号&#xff0c;并输出估计误差。程序已调通&#xff0c;可直接运行。 188 AR模型参数估计 burg法和ule-Walker法 (xiaohongshu.com)

离散数学例题——6.树和特殊图

树 树的证明 森林 同构树非同构树 生成树 有向树 二叉树遍历 哈夫曼树 特殊图 欧拉图&#xff08;一次边&#xff09; Fleury算法求欧拉回路 欧拉通路&#xff08;一笔画&#xff09; 哈密顿图&#xff08;一次点&#xff09; 哈密顿图的充分条件 哈密顿图必要条件 二部图 二部…

人工智能OCR领域安全应用措施

引言 编写目的 随着新一轮科技革命和产业变革的深入发展&#xff0c;5G、大数据、云计算、深度学习等新技术日益成为推动社会进步的核心动力。人工智能&#xff08;AI&#xff09;作为这些新技术的集大成者&#xff0c;正迅速成为新型基础设施建设的战略性支柱&#xff0c;其广…

【详识C语言】自定义类型之三:联合

本章重点 联合 联合类型的定义 联合的特点 联合大小的计算 联合&#xff08;共用体&#xff09; 联合类型的定义 联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间&#xff08;所以联合也叫共用体&#xff09;…