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

相关文章

将Mybatis升级为Mybatis-Plus的详细过程

《将Mybatis升级为Mybatis-Plus的详细过程》本文详细介绍了在若依管理系统(v3.8.8)中将MyBatis升级为MyBatis-Plus的过程,旨在提升开发效率,通过本文,开发者可实现... 目录说明流程增加依赖修改配置文件注释掉MyBATisConfig里面的Bean代码生成使用IDEA生

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

JSON Web Token在登陆中的使用过程

《JSONWebToken在登陆中的使用过程》:本文主要介绍JSONWebToken在登陆中的使用过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录JWT 介绍微服务架构中的 JWT 使用结合微服务网关的 JWT 验证1. 用户登录,生成 JWT2. 自定义过滤

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

java中使用POI生成Excel并导出过程

《java中使用POI生成Excel并导出过程》:本文主要介绍java中使用POI生成Excel并导出过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求说明及实现方式需求完成通用代码版本1版本2结果展示type参数为atype参数为b总结注:本文章中代码均为

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

tomcat多实例部署的项目实践

《tomcat多实例部署的项目实践》Tomcat多实例是指在一台设备上运行多个Tomcat服务,这些Tomcat相互独立,本文主要介绍了tomcat多实例部署的项目实践,具有一定的参考价值,感兴趣的可... 目录1.创建项目目录,测试文China编程件2js.创建实例的安装目录3.准备实例的配置文件4.编辑实例的

SpringBoot启动报错的11个高频问题排查与解决终极指南

《SpringBoot启动报错的11个高频问题排查与解决终极指南》这篇文章主要为大家详细介绍了SpringBoot启动报错的11个高频问题的排查与解决,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一... 目录1. 依赖冲突:NoSuchMethodError 的终极解法2. Bean注入失败:No qu

使用Java实现通用树形结构构建工具类

《使用Java实现通用树形结构构建工具类》这篇文章主要为大家详细介绍了如何使用Java实现通用树形结构构建工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录完整代码一、设计思想与核心功能二、核心实现原理1. 数据结构准备阶段2. 循环依赖检测算法3. 树形结构构建4. 搜索子