Tomcat启动过程是怎么样的?

news2024/11/16 5:51:52

一、Tomcat 启动流程

步骤:

1、启动tomcat,需要调用 bin/startup.bat (在linux 目录下,需要调用 bin/startup.sh),在startup.bat 脚本中,调用了catalina.bat。

2、在catalina.bat 脚本文件中,调用了BootStrap 中的main方法。

3、在BootStrap 的main 方法中调用了init方法 , 来创建Catalina 及初始化类加载器。

4、在BootStrap 的main 方法中调用了load 方法 , 在其中又调用了Catalina的load方法。

5、在Catalina 的load方法中,需要进行一些初始化的工作,并需要构造Digester 对象,用于解析 XML。

6、然后在调用后续组件的初始化操作。加载Tomcat的配置文件,初始化容器组件,监听对应的端口号,准备接受客户端请求。

1.1、相关类解析

1.1.1、Lifecycle

由于所有的组件均存在初始化、启动、停止等生命周期方法,拥有生命周期管理的特性, 所以Tomcat在设计的时候, 基于生命周期管理抽象成了一个接口 Lifecycle ,而组件 Server、Service、Container、Executor、Connector 组件 , 都实现了一个生命周期的接口,从而具有了以下生命周期中的核心方法:

1、init():初始化组件

2、start():启动组件

3、stop():停止组件

4、destroy():销毁组件

1.1.2、各组件的默认实现

上面我们提到的Server、Service、Engine、Host、Context都是接口, 下图中罗列了这些接口的默认实现类。

当前对于 Endpoint组件来说,在Tomcat中没有对应的Endpoint接口, 但是有一个抽象类 AbstractEndpoint ,其下有三个实现类:NioEndpoint、Nio2Endpoint、AprEndpoint , 这三个实现类,分别对应于前面讲解链接器 Coyote时, 提到的链接器支持的三种IO模型:NIO、NIO2、APR,Tomcat8.5版本中,默认采用的是 NioEndpoint。

ProtocolHandler:Coyote协议接口,通过封装Endpoint和Processor , 实现针对具体协议的处理功能。Tomcat按照协议和IO提供了6个实现类。

AJP协议:

1、AjpNioProtocol:采用NIO的IO模型。

2、AjpNio2Protocol:采用NIO2的IO模型。

3、AjpAprProtocol:采用APR的IO模型,需要依赖于APR库。

HTTP协议:

1、Http11NioProtocol:采用NIO的IO模型,默认使用的协议(如果服务器没有安装

APR)。

2、Http11Nio2Protocol:采用NIO2的IO模型。

3、Http11AprProtocol:采用APR的IO模型,需要依赖于APR库。

二、源码分析

众所周知,org.apache.catalina.startup.Bootstrap类的main方法是tomcat启动的入口。

public static void main(String args[]) {
    synchronized (daemonLock) {
        if (daemon == null) {
            // 在 init() 完成之前不要设置守护进程
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            // 当作为服务运行时,停止调用将在一个新线程上进行,
            // 因此请确保使用正确的类加载器来防止一系列未找到的类异常
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    }

    try {
        String command = "start";
        if (args.length > 0) {
            command = args[args.length - 1];
        }
        
        if (command.equals("startd")) {
            args[args.length - 1] = "start";
            daemon.load(args);
            daemon.start();
        } else if (command.equals("stopd")) {
            args[args.length - 1] = "stop";
            daemon.stop();
        } else if (command.equals("start")) {
            daemon.setAwait(true);
            daemon.load(args);
            daemon.start();
            if (null == daemon.getServer()) {
                System.exit(1);
            }
        } else if (command.equals("stop")) {
            daemon.stopServer(args);
        } else if (command.equals("configtest")) {
            daemon.load(args);
            if (null == daemon.getServer()) {
                System.exit(1);
            }
            System.exit(0);
        } else {
            log.warn("Bootstrap: command \"" + command + "\" does not exist.");
        }
    } catch (Throwable t) {
        // 解开异常以获得更清晰的错误报告
        if (t instanceof InvocationTargetException && t.getCause() != null) {
            t = t.getCause();
        }
        handleThrowable(t);
        t.printStackTrace();
        System.exit(1);
    }
}

 在main方法中,Bootstrap主要做三件事:初始化、加载、响应启动或停止命令

初始化

初始化过程会初始化tomcat所需的类加载器,并将catalinaLoader实例设置为当前线程的上下文类加载器。

public void init() throws Exception {
    initClassLoaders(); // 设置当前线程的上下文类加载器
    Thread.currentThread().setContextClassLoader(catalinaLoader);
}

根据配置catalina.properties,tomcat会默认初始化三种tomcat特有的类加载器。它们分别是:commonLoader、catalinaLoader和sharedLoader。不过,从源码中我们可以发现,catalinaLoader和sharedLoader默认并没有做任何配置。所以,最终只会初始化一种类加载器commonLoader。

如果在没有作个性化配置的情况下,catalinaLoader和sharedLoader都是指的是commonLoader。

commonLoader是tomcat所有类加载器的父类类加载器。

初始化类加载器之后,接下来,类加载器catalinaLoader的实例会加载类org.apache.catalina.startup.Catalina创建一个Catalina实例。

Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.getConstructor().newInstance();

接着,利用反射原理,调用Catalina的setParentClassLoader方法将sharedLoader实例设置为Catalina实例的父级类加载器。

String methodName = "setParentClassLoader";
Class<?> paramTypes[] = new Class[1];paramTypes[0] = Class.forName("java.lang.ClassLoader");
Object paramValues[] = new Object[1];paramValues[0] = sharedLoader;
Method method = startupInstance.getClass().getMethod(methodName, paramTypes);method.invoke(startupInstance, paramValues);

最后,将Catalina实例赋值给catalina的守护进程的引用catalinaDaemon。

Object catalinaDaemon = startupInstance;

至此,初始化的工作就全部完成了。

加载

在完成Bootstrap初始化的工作之后,Bootstrap接下来会开始解析main方法的入参args,生成命令字符command。

根据不同类型的命令,Bootstrap会执行不同的业务逻辑,但是这里我们只看“start”命令。

if (command.equals("start")) {
    // 设置Catalina实例的await属性为true,表示daemon会被阻塞
    // 这里的daemon也就是 Bootstrap 的实例本身
    daemon.setAwait(true);
    
    // 调用加载方法
    daemon.load(args);
    
    // 启动 Tomcat
    daemon.start();
    
    // 如果服务器为空
    if (null == daemon.getServer()) {
        // 退出程序
        System.exit(1);
    }
}

Bootstrap首先会调用load方法。

private void load(String[] arguments) throws Exception {
    // Call the load() method
    String methodName = "load";
    Object param[];
    Class<?> paramTypes[];

    if (arguments == null || arguments.length == 0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }

    Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    
    if (log.isDebugEnabled()) {
        log.debug("Calling startup class " + method);
    }
    
    method.invoke(catalinaDaemon, param);
}

由于是start,数组arguments为空,所以最后会调用Catalina的load()方法。

在这里tomcat会对Catalina实例进行初始化,其中依次包括初始化服务器Server组件、服务Service组件、连接器Connector、引擎Engine。

public void load() {
    if (loaded) {
        return;
    }
    
    loaded = true;
    long t1 = System.nanoTime();
    
    initDirs();
    // Before digester - it may be needed
    initNaming();
    
    // 设置配置源
    ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(
        Bootstrap.getCatalinaBaseFile(), getConfigFile()
    ));
    
    File file = configFile();
    // 创建并执行我们的 Digester,在这里会初始化各组件
    Digester digester = createStartDigester();
    
    try (ConfigurationSource.Resource resource = ConfigFileLoader.getSource().getServerXml()) {
        InputStream inputStream = resource.getInputStream();
        InputSource inputSource = new InputSource(resource.getURI().toURL().toString());
        inputSource.setByteStream(inputStream);
        
        digester.push(this);
        digester.parse(inputSource);
    } catch (Exception e) {
        log.warn(sm.getString("catalina.configFail", file.getAbsolutePath()), e);
        
        if (file.exists() && !file.canRead()) {
            log.warn(sm.getString("catalina.incorrectPermissions"));
        }
        return;
    }
    
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());
    
    // Stream redirection
    initStreams();
    
    // 启动新的服务器
    try {
        getServer().init();
    } catch (LifecycleException e) {
        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new java.lang.Error(e);
        } else {
            log.error(sm.getString("catalina.initError"), e);
        }
    }

    long t2 = System.nanoTime();
    if (log.isInfoEnabled()) {
        log.info(sm.getString("catalina.init", Long.valueOf((t2 - t1) / 1000000)));
    }
}

服务器Server初始化后,就得到了Server对象实例。接下来,调用Server父类LifecycleBase的init()方法。

启动

在所有初始化工作完成之后,接下来Bootstrap就会执行start()方法,启动服务器。

至此,Tomcat完成启动。

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

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

相关文章

【栈与递归】-------简单了解

1.递归的定义&#xff1a; 若一个对象部分地包含它自己&#xff0c;或用它自己给自己定义&#xff0c;则称这个对象是递归的 例如&#xff1a; 链表的结构&#xff0c;树的结构等等 若一个过程直接地或间接地调用自己&#xff0c; 则称这个过程是递归的过程。 例如&#xf…

不确定性环境下的自动驾驶汽车行为决策方法

在高度交互的复杂驾驶环境中考虑不确定性因素的影响&#xff0c;做出合理的决策&#xff0c;是当前决策规划系统须解决的主要问题之一。本文提出了一种不确定性环境下的自动驾驶汽车行为决策方法&#xff0c;为消除不确定性的影响&#xff0c;将行为决策过程转化为部分可观察马…

如何使用ssm实现基于jsp的快递管理系统的开发

TOC ssm226基于jsp的快递管理系统的开发jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规…

达梦数据库的系统视图v$db_object_cache

达梦数据库的系统视图v$db_object_cache 达梦数据库&#xff08;DM Database&#xff09;中的 V$DB_OBJECT_CACHE 视图提供了数据库对象缓存的相关信息。这些信息包括缓存中的各种数据库对象&#xff08;如表、索引、存储过程等&#xff09;的具体状态和属性。通过 V$DB_OBJEC…

基于SpringBoot的滴答拍摄影项目

你好&#xff0c;我是计算机专业的学生&#xff0c;专注于摄影项目的开发与实现。如果您对我的项目感兴趣或有任何建议&#xff0c;欢迎随时联系我。 开发语言 Java 数据库 MySQL 技术 SpringBoot框架 工具 MyEclipse、Tomcat服务器 系统展示 首页 管理员功能模块 用…

网络安全-安全渗透简介和安全渗透环境准备

文章目录 前言1. 安全渗透简介1.1 什么是安全渗透&#xff1f;1.2 安全渗透所需的工具1.3 渗透测试流程 2. 使用 Kali Linux 进行安全渗透2.1 下载ISO镜像2.2 下载VMware Workstaion软件2.3 Kali Linux简介2.4 准备Kali Linux环境2.5 Kali Linux初始配置2.6 VIM鼠标右键无法粘贴…

石碑文字全排列重组(华为od机考题)

一、题目 1.原题 有一个考古学家发现一个石碑&#xff0c; 但是很可惜&#xff0c;发现时其已经断成多段&#xff0c; 原地发现n个断口整齐的石碑碎片。 为了破解石碑内容&#xff0c; 考古学家希望有程序能帮忙计算复原后的石碑文字组合数&#xff0c; 你能帮忙吗&#xff1…

C# 三种定时器的用法

目录 1.System.Timers.Timer 2.System.Windows.Forms.Timer 3.System.Threading.Timer 4.简单的封装 这里介绍三种常用的定时器&#xff0c;方便查阅资料或直接复制使用。 1.System.Timers.Timer System.Timers.Timer 类定义了一个计时器&#xff0c;该计时器按固定间隔触…

大二必做项目贪吃蛇超详解之上篇win32库介绍

文章目录 1. 游戏背景2. 游戏效果演示3. 项目目标4. 前置知识5. Win32 API5. 1 控制台程序(Console)5. 2 控制台屏幕上的坐标 COORD5. 3 GetStdHandle5. 4 GetConsoleCursorlnfo5. 4. 1 CONSOLE_CURSOR_INFO5. 4. 2 SetConsoleCursorlnfo 5. 5 SetconsoleCursorPosition5. 6 Ge…

“汤姆猫除了不会捉杰瑞啥都会”之作为Web服务器,tomcat的常用部署方式 (Tomcat+memcached实现高可用)

目录 企业级WEB应用服务器TOMCAT一、tomcat的功能介绍1.1 安装 Tomcat1.2 tomcat的文件结构和组成1.3 生成tomcat的启动文件 二、结合反向代理实现tomcat部署2.1 常见部署方式介绍2.2 利用 nginx 反向代理实现2.3 实现tomcat中的负载均衡2.3.1 HTTP的无状态&#xff0c;有连接和…

Linux驱动学习之中断与等待队列

本篇分为设备树部分和API接口部分 设备树 想要使用中断&#xff0c;设备树中需要有两个属性&#xff1a; interrupts // 表示要使用哪一个中断, 中断的触发类型等等。 interrupt-parent // 这个中断要接到哪一个设备去? 即父中断控制器是谁 父中…

一种更快成像的新技术

斯旺西大学&#xff08;Swansea University&#xff09;的研究人员为中性原子束显微镜创造了一种新的成像方法&#xff0c;可大大加快显微镜图像的获取速度。中性原子束显微镜已成为科学研究的一个重点&#xff0c;因为它能够对商用显微镜无法成像的表面进行成像&#xff0c;例…

mysql集群从零开始搭建

文章目录 MySQL集群linux下部署mysqlmysql主从复制master配置配置slave新的slave加入延迟复制慢查询多线程原理 半同步模式原理gat模式启动半同步模式 mysql高可用之组复制&#xff08;MGR&#xff09;实现mysql组复制 mysql路由具体实现 mysql高可用之MHAMHA部署实施安装MHA软…

Codeforces Round 968 (Div. 2 ABCD1D2题) 视频讲解

A. Turtle and Good Strings Problem Statement Turtle thinks a string s s s is a good string if there exists a sequence of strings t 1 , t 2 , … , t k t_1, t_2, \ldots, t_k t1​,t2​,…,tk​ ( k k k is an arbitrary integer) such that: k ≥ 2 k \ge 2 k≥…

接口自动化测试利器,使用Rest Assured进行REST API测试

我们在做接口测试时&#xff0c;一般在代码中会使用HttpClient&#xff0c;但是HttpClient相对来讲还是比较麻烦的&#xff0c;代码量也相对较多&#xff0c;对于新手而言上手会比较难一点&#xff0c;今天我们来看下另一个接口测试工具包REST Assured REST Assured是一个流行…

Blazor官方文档学习记录

Blazor官方文档学习记录 1 官方文档2 Blazor教程-生成首个应用3 项目结构4 基础知识4.1 生态4.2 Razor组件指令顺序4.3 Razor组件的初始化方法 5 注意 1 官方文档 https://dotnet.microsoft.com/zh-cn/apps/aspnet/web-apps/blazor2 Blazor教程-生成首个应用 https://dotnet.…

Python | Linux | 解析Himawari-8/9 | Standard Data

写作前面 之前一个相关的工作需要解析Himawari-8/9 Standard Data文件&#xff0c;因为他是二进制的&#xff0c;之前没有处理过&#xff0c;导致完全摸不着头脑。在网上找了中英文搜索找了好久&#xff0c;虽然也找到了公开的解析代码&#xff0c;但是放在自己的数据这感觉总是…

趣味算法------猴子吃桃(循环,递归双重解法)

题目描述 猴子第一天摘下若干个桃子&#xff0c;当天吃了一半&#xff0c;后面又多吃一个。第二天早上又将剩下的桃子吃掉一半&#xff0c;又多吃了一个。后面每天猴子都吃了前一天剩下的一半零一个。到第十天想再吃时&#xff0c;只剩下一个桃子。求第一天共摘了多少桃子。 …

鸿蒙(API 12 Beta3版)【获取音视频元数据】音频播放与录制

使用AVMetadataExtractor可以实现从原始媒体资源中获取元数据&#xff0c;本开发指导将以获取一个音频资源的元数据作为示例&#xff0c;向开发者讲解AVMetadataExtractor元数据相关功能。视频资源的元数据获取流程与音频类似&#xff0c;由于视频没有专辑封面&#xff0c;所以…

【中仕公考怎么样】公务员备考小建议

2025年国考在即&#xff0c;掌握正确的备考方法很重要&#xff01;中仕为大家简单分享4点小技巧。 1. 在提升行测分数时&#xff0c;可以采用大量的练习题、整理题以及关注往年核心考点的方式。无论处于准备过程的哪一阶段&#xff0c;对各类题型进行深入分析并掌握相应的解题…