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 &quot;%r&quot; %s %b" /> </Host>
</Engine>
</Service>
</Server>

Server的启动/停止

通过上文可以知道,Server组件重要的就是控制Tomcat容器的启动/停止,然而启动停止并不是简单的启动JVM关闭JVM就可以了,Tomcat容器启动/停止是还必须调用容器内所有组件的生命周期方法,启动时需要所有的组件进行初始化,结束时需要所有的组件进行销毁和资源释放。

JVM的启动/停止

JVM的启动比较简单,我们在运行tomcat启动脚本的时候,会启动tomcat的Jar文件,从而启动JVM。

关于JVM的退出则稍微复杂一些,JVM退出的方式分为以下三种类型:

  1. 正常关闭:当最后一个非守护线程结束或者调用了System.exit或者通过其他特定平台的方法关闭(发送SIGINT,SIGTERM信号等)
  2. 异常关闭:运行中遇到RuntimeException异常等。
  3. 强制关闭:通过调用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 &quot;%r&quot; %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>
  1. VersionLoggerListener:在容器启动前打印各种版本信息,如JVM版本、操作系统版本、tomcat版本等信息。
  2. AprLifecycleListener:APR的生命周期处理,APR(Apache portable Run-time libraries,Apache可移植运行库)的目的如其名称一样,主要为上层的应用程序提供一个可以跨越多操作系统平台使用的底层支持接口库。
  3. JreMemoryLeakPreventionListener:用于处理上下文类加载器可能出现的内存泄露问题,启动Java内存自动回收任务,每小时触发FullGC。
  4. GlobalResourcesLifecycleListener:Tomcat启动时实例化JNDI资源的MBean,Tomcat停止时销毁MBean.
  5. ThreadLocalLeakPreventionListener:在Context关闭的时候清空线程上下文,防止ThreadLocal内存泄露。

我是御狐神,欢迎大家关注我的微信公众号:wzm2zsd

本文最先发布至微信公众号,版权所有,禁止转载!

学习Tomcat(二)之容器概览的更多相关文章

  1. Spring学习总结二——SpringIOC容器二

    一:指定bean的依赖关系 例如examplebean对象依赖examplebean1对象,那么在创建examplebean对象之前就 需要先创建examplebean1对象. 1:创建Example ...

  2. Odoo 学习 【二】Environment 概览

    Environment 参考链接: http://odoo-new-api-guide-line.readthedocs.io/en/latest/environment.html#environme ...

  3. tomcat(5)servlet容器

    [0]README 0.0)本文部分文字描写叙述转自:"深入剖析tomcat",旨在学习 tomcat(5)servlet容器 的基础知识. 0.1)intro to servle ...

  4. 学习Tomcat(一)之容器概览

    Tomcat是Apache软件基金会的一个顶级项目,由Apache.Sun和其它一些公司及个人共同开发,是目前比较流行的Web服务器之一.Tomcat是一个开源的.小型的轻量级应用服务器,具有占用系统 ...

  5. 跟着刚哥学习Spring框架--Spring容器(二)

    Spring容器 启动Spring容器(实例化容器) -- IOC容器读取Bean配置创建Bean实例之前,必须对它进行实例化(加载启动),这样才可以从容器中获取Bean的实例并使用.  Bean是S ...

  6. 学习Tomcat(二)

    一. Java简介 JDK: 面向开发人员使用的SDK,提供Java的开发环境和运行环境 SDK: 软件开发包,包括函数库.编译程序等 JRE: Java的运行环境,面向Java的使用者,不是开发者 ...

  7. JSP学习 —— 开篇:JSP,servlet容器,Tomcat,servlet容器之间的关系

    JSP(JAVA SERVER PAGE)的缩写,其本身就是servlet的简化,是一种动态网页标准,其特点是在HTML代码中嵌入JAVA代码,JSP标签或用户标签来生成网页.至于它为什么会出现,主要 ...

  8. Docker 与 K8S学习笔记(十 二)容器间数据共享

    数据共享是volume的关键特性,今天我们来看一下通过volume实现容器与host.容器与容器之间共享数据. 一.容器与host共享数据 在上一篇中介绍到的bind mount和docker man ...

  9. 烂泥:学习tomcat之通过shell批量管理多个tomcat

    本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 公司的业务是使用tomcat做web容器,为了更有效的利用服务器的性能,我们一般部署多个 ...

  10. 标准模板库(STL)学习探究之vector容器

    标准模板库(STL)学习探究之vector容器  C++ Vectors vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库.vector之所以被 ...

随机推荐

  1. UVa11054 Gergovia的酒交易(数学归纳法)

    直线上有\(n\)个等距村庄,每个村庄要么买酒,要么卖酒.设第\(i\)个村庄对酒的需求为\(A_i\)(\(-1000 \leqslant A_i \leqslant 1000\)),其中\(A_i ...

  2. Linux下MySQL多实例部署记录

    什么是MySQL多实例 简单地说,Mysql多实例就是在一台服务器上同时开启多个不同的服务端口(3306.3307),同时运行多个Mysql服务进程,这些服务进程通过不同的socket监听不同的服务端 ...

  3. rabbitMQ通过@RabbitListener和配置文件XML创建exchange及队列绑定关系

    1.@RabbitListener 2.配置文件 <rabbit:fanout-exchange name="fanoutExchange" xmlns="http ...

  4. 【Java】jeesite初始配置以及代码生成工具的使用

    jeesite简单使用 首先去技术服务与支持.版本区别一览表 - JeeSite 4.x找到源码下载的部分 JeeSite 源码下载:https://gitee.com/thinkgem/jeesit ...

  5. 题解 Connect

    传送门 各种骗分无果,特殊性质还手残写挂了-- 首先完全图上直接输出边权 \(\times (n-2)\) 就行了,然而我脑残乘的 \(n-1\) 看数据范围肯定是状压,但是压边肯定炸了,考虑压点 因 ...

  6. DotNetCore深入了解:HTTPClientFactory类

    一.HttpClient使用 在C#中,如果我们需要向某特定的URL地址发送Http请求的时候,通常会用到HttpClient类.会将HttpClient包裹在using内部进行声明和初始化,如下面的 ...

  7. Flink提交流程和架构

    一.Flink提交任务的流程 Flink任务提交后,Client向HDFS上传Flink的jar包和配置,之后向Yarn ResourceManager提交任务,ResourceManager分配Co ...

  8. 刷题-力扣-50. Pow(x, n)

    50. Pow(x, n) 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/powx-n/ 著作权归领扣网络所有.商业转载请联系官方授 ...

  9. HTML5存储 ——Web Storage(localStorage 和 sessionStorage)

    一.localStorage对象临时储存API 方法: 1.localStorage.setItem(key,value)---设置存储内容 2.localStorage.getItem(key)-- ...

  10. Spring Boot +Vue 项目实战笔记(三):数据库的引入

    这一篇的主要内容是引入数据库并实现通过数据库验证用户名与密码. 一.引入数据库 之前说过数据库的采用是 MySQL,算是比较主流的选择,从性能和体量等方面都比较优秀,当然也有一些弊端,但数据库不是我们 ...