Tomcat分析一——Tomcat的顶层结构及启动过程

2024-05-24 09:32

本文主要是介绍Tomcat分析一——Tomcat的顶层结构及启动过程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.1 Tomcat的顶层结构

​ Tomcat最顶层容器叫Server,代表整个服务器,Server中至少有一个Service,用于提供服务。

​ Service主要包含两部分:

Connector:用于处理连接相关的事情,并提供Socket与request、response的转换

Container:用于封装和管理Servlet及具体处理request请求。

一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以有多个Connector。(因为一个服务可以有多个连接,如同时提供http和https连接,也可以提供相同协议不同端口的连接)

​ Tomcat中的Server由Catalina管理,Catalina是整个Tomcat的管理类,里面的三个方法load、start、stop分别管理整个服务器的生命周期,load方法用于根据Tomcat的 conf/server.xml 文件创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,stop方法用于停止服务器,start和sto方法内部分别调用Server的start和stop方法,load内部调用了Server的init方法。

​ 这三个方法都会按容器的结构逐层调用相应的方法,如Server的start方法中会调用所有Service中的start方法,Service中的start方法会调用所有包含的Connectors和Container的start方法,这样整个服务器就启动了。init和stop方法也一样,这就是Tomcat的生命周期的管理方式。

​ Catalina的await方法直接调用了Server的await方法,作用是进入一个循环,让主线程不会退出。

​ Tomcat的入口Bootstrap中,Bootstrap的作用类似CatalinaAdaptor,具体处理过程还是使用Catalina完成。好处是把启动的入口和具体的管理类分开,方便创建多种启动方式,每种方式只需要写一个相应的CatalinaAdaptor。

1.2 Bootstrap的启动过程

正常启动Tomcat调用Bootstrap的main方法,主要分为两部分:

1.2.1 首先新建Bootstrap,并执行init方法初始化

init方法初始化ClassLoader,并用ClassLoader创建Catalina实例,赋给catalinaDaemon变量,后面对命令的操作都使用catalinaDaemon具体执行。

1.2.2 处理main方法传入的命令,如果args参数为空,默认执行start

​ start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。都调用了Catalina的相应方法进行具体执行,是用反射来调用的。

public static void main(String[] args) {if (daemon == null) { // 先新建一个BootstrapBootstrap bootstrap = new Bootstrap();try {//初始化ClassLoader,并用ClassLoader创建Catalina实例,赋给catalinaDaemon变量bootstrap.init();} catch (Throwable var3) {handleThrowable(var3);var3.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();} 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 var4) {Throwable t = var4;if (var4 instanceof InvocationTargetException && var4.getCause() != null) {t = var4.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}
}

​ start方法/setAwait方法/load方法——>根据反射调用catalina的start/setAwait/load方法

​ 首先判断catalinaDaemon是否初始化,否则调用init方法进行初始化。然后使用Method进行反射调用Catalina的start方法。((Catalina)catalinaDaemon).start()。

​ Method是reflect包的类,代表一个具体的方法,可以使用其invoke方法执行代表的方法,第一个参数是Method方法所在的实体,第二个参数是可变参数,用于Method方法执行时需要的参数。

​ catalina.start()——>server.start()——>service.start()——>connector.start()/container.start()

public void start() throws Exception {if (this.catalinaDaemon == null) {this.init();}Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);method.invoke(this.catalinaDaemon, (Object[])null);
}public void setAwait(boolean await) throws Exception {Class<?>[] paramTypes = new Class[]{Boolean.TYPE};Object[] paramValues = new Object[]{await};Method method = this.catalinaDaemon.getClass().getMethod("setAwait",paramTypes);method.invoke(this.catalinaDaemon, paramValues);}
private void load(String[] arguments) throws Exception {String methodName = "load";Object[] param;Class[] paramTypes;if (arguments != null && arguments.length != 0) {paramTypes = new Class[]{arguments.getClass()};param = new Object[]{arguments};} else {paramTypes = null;param = null;}Method method = this.catalinaDaemon.getClass().getMethod(methodName,paramTypes);if (log.isDebugEnabled()) {log.debug("Calling startup class " + method);}method.invoke(this.catalinaDaemon, param);}

1.3 Catalina的启动过程

Catalina的启动主要是调用setAwait、load和start方法完成的。

​ setAwait 方法用于设置Server启动完成后是否进入等待状态的标志,true则进入;

​ load 方法用于加载配置文件,创建并初始化Server;

​ start 方法用于启动服务器。

1.3.1 setAwait(boolean)方法

setAwait方法设置await属性的值,await属性在start方法中 服务器启动后使用它判断是否进入等待状态。

public class Catalina {protected static final StringManager sm = StringManager.getManager("org.apache.catalina.startup");protected boolean await = false;protected String configFile = "conf/server.xml";public void setAwait(boolean b) {this.await = b;}

1.3.2 load 方法

​ load方法根据conf/server.xml创建Server对象,并赋值给server属性,解析操作使用Tomcat开源的Digester完成的,然后调用server的init方法

public void load() {long t1 = System.nanoTime();this.initDirs();this.initNaming();Digester digester = this.createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;.....file = this.configFile();// conf/server.xmlinputStream = new FileInputStream(file);inputSource = new InputSource(file.toURI().toURL().toString());inputSource.setByteStream((InputStream)inputStream);digester.push(this);digester.parse(inputSource);......this.getServer().setCatalina(this);this.getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());this.getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());this.initStreams();try {this.getServer().init();} catch (LifecycleException var24) {}long t2 = System.nanoTime();if (log.isInfoEnabled()) {log.info("Initialization processed in " + (t2 - t1) / 1000000L + " ms");}}

1.3.3 start方法

Catalina的start方法主要调用了server的start方法启动服务器,并根据await属性判断是否让程序进入等待状态

  1. 先判断Server是否存在,不存在则调用load方法初始化Server

  2. 然后调用Server的start方法启动服务器

  3. 最后注册关闭钩子并根据await属性判断是否进入等待状态,由于之前设置为true,所以需要等待

  4. 进入等待状态会调用await和stop两个方法,await方法直接调用了Server的await方法,会执行一个while循环,这样程序就停在await方法,当await方法的while循环退出时,就会执行stop方法,从而关闭服务器。

    server的await方法里,while循环根据volatile类型的stopAwait是否为true停止,默认false,一直循环
    
public void start() {if (this.getServer() == null) {this.load();}.....    try {this.getServer().start();} catch (LifecycleException var7) {try {this.getServer().destroy();} return;}if (this.useShutdownHook) {if (this.shutdownHook == null) {this.shutdownHook = new Catalina.CatalinaShutdownHook();}Runtime.getRuntime().addShutdownHook(this.shutdownHook);LogManager logManager = LogManager.getLogManager();if (logManager instanceof ClassLoaderLogManager) {((ClassLoaderLogManager)logManager).setUseShutdownHook(false);}}if (this.await) {this.await();this.stop();}}
}

1.4 Server的启动过程

类继承和实现的关系图

Server接口的addService(Service)、removeService(Service)添加和删除Service、Server的init方法和start方法分别循环调用每个Service的init方法和start方法启动所有Service。

Server默认的实现是StandardServerStandardServer又继承自LifecycleMBeanBase

LifecycleMBeanBase又继承自LifecycleBase

LifecycleBase中定义了init和start方法

LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法,这两个方法都是模版方法,由子类具体实现,

public final synchronized void init() throws LifecycleException {if (!this.state.equals(LifecycleState.NEW)) {this.invalidTransition("before_init");}try {this.setStateInternal(LifecycleState.INITIALIZING, (Object)null, false);this.initInternal();this.setStateInternal(LifecycleState.INITIALIZED, (Object)null, false);} catch (Throwable var2) {ExceptionUtils.handleThrowable(var2);this.setStateInternal(LifecycleState.FAILED, (Object)null, false);}
}protected abstract void initInternal() throws LifecycleException;public final synchronized void start() throws LifecycleException {try {this.setStateInternal(LifecycleState.STARTING_PREP, (Object)null,false);this.startInternal();if (this.state.equals(LifecycleState.FAILED)) {this.stop();} else if (!this.state.equals(LifecycleState.STARTING)) {this.invalidTransition("after_start");} else {this.setStateInternal(LifecycleState.STARTED, (Object)null, false);}protected abstract void startInternal() throws LifecycleException;

所以调用StandardServer的init方法和start方法会执行StandardServer自己的initInternal方法和startInternal方法,里面又分别执行所有的services的init和start方法。

protected void initInternal() throws LifecycleException {super.initInternal();MBeanFactory factory = new MBeanFactory();factory.setContainer(this);this.onameMBeanFactory = this.register(factory, "type=MBeanFactory");this.globalNamingResources.init();for(int i = 0; i < this.services.length; ++i) {this.services[i].init();}
}
protected void startInternal() throws LifecycleException {this.fireLifecycleEvent("configure_start", (Object)null);this.setState(LifecycleState.STARTING);this.globalNamingResources.start();synchronized(this.servicesLock) {for(int i = 0; i < this.services.length; ++i) {this.services[i].start();}}
}

StandardServer的await方法

await方法处理流程,省略了一些处理异常、关闭Socket及对接收到数据处理的代码。

首先判断端口号port,然后根据port的值分为三种处理方法:

  1. port为-2,会直接退出,不进行循环

  2. port为-1,会进入while(!stopAwait)的循环,且内部没有berak跳出的语句,stopAwait标志只有调用了stop方法才会设置为true,所以port为-1时只有在外部调用stop方法才会退出循环

  3. port为其他值,会进入一个while(!stopAwait)循环,同时会在port所在的端口启动一个ServerSocker监听关闭命令,接收到了则使用break跳出循环。

    ​ 这里的端口port和关闭命令shutdown是在conf/server.xml文件中配置Server时设置的。默认设置如下

    <!-- server.xml -->
    <Server port="8005" shutdown="SHUTDOWN">
    

    ​ 这时在8005端口监听"SHUTDOWN"命令,接收到了就会关闭Tomcat,如果不想使用网络命令来关闭服务器可以将端口设置为-1。

    ​ await方法中从端口接受到数据后还会进行处理,如果接收到的数据中有ASCII码小于32的(ASCII中32以下的为控制符)则从小于32的字符截断并丢弃后面的数据。

1.5 Service的启动过程

Service默认的实现是StandardServiceStandardService也继承自LifecycleMBeanBase

StandardService的init方法和start方法最终会执行StandardServer自己的initInternal方法和startInternal方法

initInternal方法主要调用engine、mapperListener、executor和connector的init方法

​ mapperListener是Mapper的监听器,监听container容器的变化,

​ executors是用在connectors中管理线程的线程池

protected void initInternal() throws LifecycleException {super.initInternal();if (engine != null) {engine.init();}// Initialize any Executorsfor (Executor executor : findExecutors()) {if (executor instanceof JmxEnabled) {((JmxEnabled) executor).setDomain(getDomain());}executor.init();}// Initialize mapper listenermapperListener.init();// Initialize our defined Connectorssynchronized (connectorsLock) {for (Connector connector : connectors) {connector.init();}}
}

startInternal主要调用engine、mapperListener、executor和connector的start方法

protected void startInternal() throws LifecycleException {setState(LifecycleState.STARTING);// Start our defined Container firstif (engine != null) {synchronized (engine) {engine.start();}}synchronized (executors) {for (Executor executor: executors) {executor.start();}}mapperListener.start();// Start our defined Connectors secondsynchronized (connectorsLock) {for (Connector connector: connectors) {// If it has already failed, don't try and start itif (connector.getState() != LifecycleState.FAILED) {connector.start();}}}
}

现在整个Tomcat服务器就启动了,整个启动流程
在这里插入图片描述

这篇关于Tomcat分析一——Tomcat的顶层结构及启动过程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/998027

相关文章

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

PLsql Oracle 下载安装图文过程详解

《PLsqlOracle下载安装图文过程详解》PL/SQLDeveloper是一款用于开发Oracle数据库的集成开发环境,可以通过官网下载安装配置,并通过配置tnsnames.ora文件及环境变... 目录一、PL/SQL Developer 简介二、PL/SQL Developer 安装及配置详解1.下

使用Navicat工具比对两个数据库所有表结构的差异案例详解

《使用Navicat工具比对两个数据库所有表结构的差异案例详解》:本文主要介绍如何使用Navicat工具对比两个数据库test_old和test_new,并生成相应的DDLSQL语句,以便将te... 目录概要案例一、如图两个数据库test_old和test_new进行比较:二、开始比较总结概要公司存在多

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程

《在Java中使用ModelMapper简化Shapefile属性转JavaBean实战过程》本文介绍了在Java中使用ModelMapper库简化Shapefile属性转JavaBean的过程,对比... 目录前言一、原始的处理办法1、使用Set方法来转换2、使用构造方法转换二、基于ModelMapper

Windows设置nginx启动端口的方法

《Windows设置nginx启动端口的方法》在服务器配置与开发过程中,nginx作为一款高效的HTTP和反向代理服务器,被广泛应用,而在Windows系统中,合理设置nginx的启动端口,是确保其正... 目录一、为什么要设置 nginx 启动端口二、设置步骤三、常见问题及解决一、为什么要设置 nginx