学习Tomcat(二)之容器概览
Tomcat容器的Server模块有管理容器的启动和关闭、管理了容器内的服务组件Service、管理了全局JNDI资源的功能,对Tomcat容器的生命周期管理有重要意义。Tomcat的服务组件则是Tomcat的两个核心组件连接器和servlet容器之间的桥梁。本文会对Tomcat容器的服务器组件Server和服务组件Service进行介绍。
服务器组件Server
我们知道Tomcat容器启动之后就可以一直保持服务,即使请求出现异常也不会退出,只有在收到特定的容器关闭命令时才会退出。Tomcat容器是怎么实现容器的启动?启动之后是如何保证容器一直保持运行?在收到容器关闭命令的时候怎么优雅关闭的呢?这就是Tomcat容器中的Server的功能了。
每个Tomcat容器都会唯一包含一个Server组件,对应于Tomcat安装文件夹下面的server.xml。下面为Tomcat10安装包中conf/server.xml的默认配置。分析xml可知,server节点有 port和shutdown属性,包含Listener、GlobalNamintResources和Service三部分子节点。下文我们会分别对这些内容进行介绍。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<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>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
</Server>
Server的启动/停止
通过上文可以知道,Server组件重要的就是控制Tomcat容器的启动/停止,然而启动停止并不是简单的启动JVM关闭JVM就可以了,Tomcat容器启动/停止是还必须调用容器内所有组件的生命周期方法,启动时需要所有的组件进行初始化,结束时需要所有的组件进行销毁和资源释放。
JVM的启动/停止
JVM的启动比较简单,我们在运行tomcat启动脚本的时候,会启动tomcat的Jar文件,从而启动JVM。
关于JVM的退出则稍微复杂一些,JVM退出的方式分为以下三种类型:
- 正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等)
- 异常关闭:运行中遇到RuntimeException异常等。
- 强制关闭:通过调用Runtime.halt方法或者是在操作系统中直接kill(发送SIGKILL信号)掉JVM进程

对于正常关闭和异常关闭,JVM都有机会执行关闭的Hook方法,对于强制关闭则不一定会执行关闭时的hook方法。所以我们在日常使用中应该尽量避免使用kill -9等方法退出JVM。
JVM注册Shutdown Hook的方法如下所示:
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
//
}
});
Tomcat容器启动的时候会通过Runtime.getRuntime().addShutdownHook(Runnable run)方法向JVM注册关闭回调方法CatalinaShutdownHook,从而实现容器的优雅关闭。
Tomcat关闭接口
我们上面讲了JVM退出的情况下Tomcat怎么实现优雅的关闭,Tomcat也可以主动关闭程序,我们在配置server.xml文件的时候,会指定server的port和shutdown指令,在需要关闭Tomcat容器的时候,我们只需要向指定端口发送关闭指令,Tomcat就会主动退出服务。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
</Server>
生命周期的控制
Tomcat中需要实现声明周期管理的组件都会实现Lifecycle接口。通过上文我们知道Tomcat的启动/停止是由Server控制的,那么Server是如何通知容器内的其它组件(如container、connector)启动/停止相关事件的呢?我们先看看Tomcat的结构图,我们可以看到Tomcat容器的组件之间是一层层包含关系,一个Server包含多个Service,一个Service包含多个Container等等。
Tomcat容器在关闭的时候会通知所有的子组件(service组件)容器关闭事件,service组件再通知它的所有子组件容器关闭事件。事件通过父子关系层层传递到各个组件,从而实现组件之间的生命周期管理。

事实上Tomcat容器的生命周期事件不仅仅包含启动/关闭,而是更详细的划分了启动关闭的各个阶段,分为以下代码示例中的各个事件。
public static final String BEFORE_INIT_EVENT = "before_init";
public static final String AFTER_INIT_EVENT = "after_init";
public static final String START_EVENT = "start";
public static final String BEFORE_START_EVENT = "before_start";
public static final String AFTER_START_EVENT = "after_start";
public static final String STOP_EVENT = "stop";
public static final String BEFORE_STOP_EVENT = "before_stop";
public static final String AFTER_STOP_EVENT = "after_stop";
public static final String AFTER_DESTROY_EVENT = "after_destroy";
public static final String BEFORE_DESTROY_EVENT = "before_destroy";
public static final String PERIODIC_EVENT = "periodic";
public static final String CONFIGURE_START_EVENT = "configure_start";
public static final String CONFIGURE_STOP_EVENT = "configure_stop";
Service服务组件
Server中Service的配置如下所示,Service组件包含两种组件:连接器和Servlet容器,其中servlet容器只有一个,连接器可以由多个。多个连接器可以使Tomcat为多种不同的请求协议提供服务,比如一个处理HTTP请求,另外一个处理HTTPS请求。
连接器负责将Socket请求解析为Request和Response,而Servlet容器则负责根据业务逻辑处理请求中的Request和Response,Service服务组件则负责把二者关联起来。我会在其它文章中详细介绍Servlet容器和连接器Connector。
每个连接器组件Connector都可以指定一个Servlet容器处理其解析得到的Request和Response,所以Service的功能比较简单,就是为Service中的每个组件设置Servlet容器。
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
Server的其它配置
全局资源GlobalNamingResources
提供了容器级别的JNDI资源配置。比如下面的默认配置,就提供了Tomcat用户数据的JNDI,存储在conf/tomcat-users.xml中。容器资源对容器的依赖性比较高,现在的使用场景比较少。
<GlobalNamingResources>
<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>
监听器Listener
监听器用来监听容器的特定事件,如容器的启动关闭事件等。如下所示,默认的server.xml中包含了5个监听器,我们接下来会简单介绍默认监听器的功能。
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
</Server>
- VersionLoggerListener:在容器启动前打印各种版本信息,如JVM版本、操作系统版本、tomcat版本等信息。
- AprLifecycleListener:APR的生命周期处理,APR(Apache portable Run-time libraries,Apache可移植运行库)的目的如其名称一样,主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。
- JreMemoryLeakPreventionListener:用于处理上下文类加载器可能出现的内存泄露问题,启动Java内存自动回收任务,每小时触发FullGC。
- GlobalResourcesLifecycleListener:Tomcat启动时实例化JNDI资源的MBean,Tomcat停止时销毁MBean.
- ThreadLocalLeakPreventionListener:在Context关闭的时候清空线程上下文,防止ThreadLocal内存泄露。
我是御狐神,欢迎大家关注我的微信公众号:wzm2zsd

本文最先发布至微信公众号,版权所有,禁止转载!
学习Tomcat(二)之容器概览的更多相关文章
- Spring学习总结二——SpringIOC容器二
一:指定bean的依赖关系 例如examplebean对象依赖examplebean1对象,那么在创建examplebean对象之前就 需要先创建examplebean1对象. 1:创建Example ...
- Odoo 学习 【二】Environment 概览
Environment 参考链接: http://odoo-new-api-guide-line.readthedocs.io/en/latest/environment.html#environme ...
- tomcat(5)servlet容器
[0]README 0.0)本文部分文字描写叙述转自:"深入剖析tomcat",旨在学习 tomcat(5)servlet容器 的基础知识. 0.1)intro to servle ...
- 学习Tomcat(一)之容器概览
Tomcat是Apache软件基金会的一个顶级项目,由Apache.Sun和其它一些公司及个人共同开发,是目前比较流行的Web服务器之一.Tomcat是一个开源的.小型的轻量级应用服务器,具有占用系统 ...
- 跟着刚哥学习Spring框架--Spring容器(二)
Spring容器 启动Spring容器(实例化容器) -- IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化(加载启动),这样才可以从容器中获取Bean的实例并使用. Bean是S ...
- 学习Tomcat(二)
一. Java简介 JDK: 面向开发人员使用的SDK,提供Java的开发环境和运行环境 SDK: 软件开发包,包括函数库.编译程序等 JRE: Java的运行环境,面向Java的使用者,不是开发者 ...
- JSP学习 —— 开篇:JSP,servlet容器,Tomcat,servlet容器之间的关系
JSP(JAVA SERVER PAGE)的缩写,其本身就是servlet的简化,是一种动态网页标准,其特点是在HTML代码中嵌入JAVA代码,JSP标签或用户标签来生成网页.至于它为什么会出现,主要 ...
- Docker 与 K8S学习笔记(十 二)容器间数据共享
数据共享是volume的关键特性,今天我们来看一下通过volume实现容器与host.容器与容器之间共享数据. 一.容器与host共享数据 在上一篇中介绍到的bind mount和docker man ...
- 烂泥:学习tomcat之通过shell批量管理多个tomcat
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 公司的业务是使用tomcat做web容器,为了更有效的利用服务器的性能,我们一般部署多个 ...
- 标准模板库(STL)学习探究之vector容器
标准模板库(STL)学习探究之vector容器 C++ Vectors vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被 ...
随机推荐
- 2020Android高级开发面试题以及答案整理,持续更新中~
本篇收录了一些大厂面试中经常会遇到的经典面试题,并且我做好了整理分类.虽然今年的金九银十已经过去了,但是可以为明年的金三银四做准备啊,相信每一个跳槽季都有很多的前端开发者蠢蠢欲动,通过对本篇知识的整理 ...
- Java_classpath
Java_classpath 什么是classpath? classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索class. 因为Java是编译型语言,源码文件是.java,而编译后的 ...
- 浅谈C#取消令牌CancellationTokenSource
前言 相信大家在使用C#进行开发的时候,特别是使用异步的场景,多多少少会接触到CancellationTokenSource.看名字就知道它和取消异步任务相关的,而且一看便知大名鼎鼎的Cancella ...
- Docker部署netcore web实践
1. 新建一个netcore的项目 2. 我们到项目的生成输出目录下,创建一个Dockerfile文件 3. 编辑Dockerfile文件 备注:红线圈住的地方,就是你生成的netcore的程序名称 ...
- How to name a slf4j logger
Use logger in a non-static context: Logger logger = LoggerFactory.getLogger(this.getClass().getName( ...
- Shell-15-脚本练习
批量生成随机字符串文件名 # 用for循环在 /test 目录下批量创建10个html文件,其中每个文件需要包含10个随机小写字符加固定字符串 alnk #!/bin/bash ########### ...
- 零基础学Java之Java学习笔记(四):运算符
算术运算符: 算术运算符是对数值类型的变量进行运算的,在 Java 程序中使用的非常多. 运算符 说明 例子 结果 + 加法-相加运算符两侧的值 9+9 18 - 减法-左操作数减去右操作数 10 ...
- Razor Pages
学习Razor Pages笔记 学习内容:https://learnrazorpages.com Razor页面都是以.cshtml结尾,其中内容页面必须具有以下三个特征: 1,文件名首位不能是下划线 ...
- Emlog V6.0.0代码审计笔记
前言 emlog是一套基于PHP和MySQL的博客及CMS建站系统. emlog v6.0.0存在后台SQL注入漏洞. 分析 官网下载emlog最新版v6.0.0,本地搭建. 前台功能不多,参数基本都 ...
- STM32—ADC多通道采集电压
文章目录 ADC详解 程序说明 函数主体 引脚配置 ADC和DMA配置 主函数 ADC详解 前面的博客中详细介绍了STM32中ADC的相关信息,这篇博客是对ADC内容的一个总结提升,ADC的详细介绍: ...