深入剖析Tomcat(十五、十六) 关闭钩子,保证Tomcat的正常关闭

news2024/12/24 20:39:30

《深入剖析Tomcat》书中第十五章讲解了如何通过配置XML的方式来配置Tomcat的各个组件,并通过Digester库来解析XML。我们常操作的xml文件应该就是 server.xml这个文件,当在一台机器上部署多个Tomcat时,就必须修改连接器和 [“关闭Tomcat”程序] 监听的端口号,以解决端口冲突,即8005和8080 两个端口号。

<Server port="8005" shutdown="SHUTDOWN" debug="0">


    <!-- Uncomment these entries to enable JMX MBeans support -->
    <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener"
              debug="0"/>
    <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener"
              debug="0"/>

    <!-- Global JNDI resources -->
    <GlobalNamingResources>

        <!-- Test entry for demonstration purposes -->
        <Environment name="simpleValue" type="java.lang.Integer" value="30"/>

        <!-- Editable user database that can also be used by
             UserDatabaseRealm to authenticate users -->
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved">
        </Resource>
        <ResourceParams name="UserDatabase">
            <parameter>
                <name>factory</name>
                <value>org.apache.catalina.users.MemoryUserDatabaseFactory</value>
            </parameter>
            <parameter>
                <name>pathname</name>
                <value>conf/tomcat-users.xml</value>
            </parameter>
        </ResourceParams>
    </GlobalNamingResources>

    <Service name="Tomcat-Standalone">

        <Connector className="org.apache.catalina.connector.http.HttpConnector"
                   port="8080" minProcessors="5" maxProcessors="75"
                   enableLookups="true" redirectPort="8443"
                   acceptCount="10" debug="0"/>

        <Engine name="Standalone" defaultHost="localhost" debug="0">
            <Logger className="org.apache.catalina.logger.FileLogger"
                    prefix="catalina_log." suffix=".txt"
                    timestamp="true"/>
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   debug="0" resourceName="UserDatabase"/>
            <Host name="localhost" debug="0" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
                <Logger className="org.apache.catalina.logger.FileLogger"
                        directory="logs" prefix="localhost_log." suffix=".txt"
                        timestamp="true"/>
            </Host>
        </Engine>
    </Service>
</Server>

第十五章的内容就不展开讲了,我们直接来到第十六章:“关闭钩子”。

上一章中,讲到了Tomcat可以通过接收一条“关闭消息”的方式来正常关闭Tomcat(执行StandardServer的stop方法),代码逻辑如下图所示

server.await() 方法监听8005端口

但是正常情况下我们想停止Tomcat时,很少会向Tomcat的8005端口发送一条关闭消息(甚至很多同学都不知道还有这个机制),而是直接kill掉Tomcat对应的java进程,如果直接kill 进程的话,上图中的代码就根本不会走到第3步,“关闭方式1”这条消息也不会打印出来(已亲测过)。

那Tomcat是如何保证关闭流程能够在kill 进程时也能触发的呢?

这就得益于Java的设计了,Java 为程序员提供了一种优雅的方法,可以在关闭JVM进程时执行一些代码,这样就能确保那些负责善后处理的代码肯定能够执行。

在Java中,虚拟机会对两类事件进行响应,然后执行关闭操作:

  • 当调用System.exit() 方法或程序的最后一个非守护进程线程退出时,应用程序正常退出。
  • 用户突然强制虚拟机中断运行,例如用户按CTRL+C 快捷键或kill掉这个java进程。

虚拟机在执行关闭操作时,会经过以下两个阶段:

  1. 虚拟机启动所有已经注册的关闭钩子(如果有的话)。关闭钩子是先前已经通过Runtime类注册的线程,所有的关闭钩子会并发执行,直到完成任务;
  2. 虚拟机根据情况调用所有没有被调用过的终结器 (finalizer)。

本章重点说明第一个阶段,因为该阶段允许程序员告诉虚拟机在应用程序关闭时需要执行哪些清理代码。StandardServer#Stop() 方法就可以安排在这个阶段。

关闭钩子

上面提到的关闭钩子是啥呢?其实很简单,任何一个继承了Thread类的线程类都可以当做一个关闭钩子,钩子怎么生效呢?使用下面这行代码就能生效

Runtime.getRuntime().addShutdownHook(关闭钩子的对象);

下面我们设计一个关闭钩子,来优雅的关闭Tomcat。

先定义钩子类

该类是Bootstrap启动类的一个内部类,继承了Thread类,拥有一个Server组件实例的引用,以便在run方法中调用Server组件的stop()方法。

然后是将这个关闭钩子注册到当前JVM进程中

我这里将它安排在 server组件的 start() 方法后(即Tomcat启动后)注册进来。如下图所示

然后通过Bootstrap启动Tomcat类后,kill 进程,可以看到关闭钩子类的run方法被执行了,其实就是,关闭钩子对应的线程被创建了,然后执行了该线程的run方法。而“关闭方式1”这条日志和添加钩子前一样,不会打印。

测试结束,可以看到咱们自定义的关闭钩子MyShutDownHook成功完成使命:在被杀进程时执行了Server组件的stop() 方法。

注意!关闭钩子是属于Java的,并不单属于Tomcat,所有基于java的程序都可以定义关闭钩子,大家千万不要被局限了。

kill 与 kill -9

kill 的杀,比较有同情心,杀之前问问该进程有没有遗言,有遗言的话就说,于是进程开始巴拉巴拉交代遗言(执行所有关闭钩子),交代完之后,系统刽子手就手起刀落,杀死该进程。特殊情况下,该进程是个话痨,遗言巴拉了半天也不完,于是系统刽子手就不管它了,于是该进程就逃过一截 (キ`゚Д゚´)!!  ,这也就是我们常见的 kill 一个进程却 kill 不掉的情况。

kill -9 的杀,是个无情的刽子手,用户一发出 kill -9的执行,系统刽子手二话不说咔嚓一下就将该进程杀死了,管你有什么遗言没有,就是不给你说的机会。所以说执行 kill -9 命令要慎重,避免该进程本来有遗言要说却没说出来,造成对应故障。

Tomcat是怎么使用关闭钩子的

Tomcat的启动入口其实是org.apache.catalina.startup.Catalina类,该类中有一个自定义的关闭钩子,如下所示,功能和我们上面定义的一样,都是去调用server组件的stop方法。

然后在Catalina的start()方法中注册了这个钩子,在注册钩子前其实已经执行了server组件的start()方法,这个我没截出来,你知道即可。

在stop() 方法中做了移除关闭钩子的操作

以上就是Tomcat使用关闭钩子的方式。

为什么在stop() 方法中要移除关闭钩子呢?

这里通过几个实验来说明问题

前提布置

基于我们开篇的自定义钩子的代码,在两条日志中均加上线程名,1.正常关闭Tomcat的日志  2.通过关闭钩子关闭Tomcat的日志

StandardServer的stop()方法加一段线程阻塞的代码,模拟长时间的代码执行。在入口处加上日志

然后进行实验

实验一:仅通过kill 命令关闭Tomcat

关闭钩子触发,stop()方法正常运行,一切正常。

实验二:仅通过发送“shutdown”消息关闭Tomcat

还是通过Stopper类来发送shutdown消息

public class Stopper {

    public static void main(String[] args) {
        // the following code is taken from the Stop method of
        // the org.apache.catalina.startup.Catalina class
        int port = 8005;
        try {
            Socket socket = new Socket("127.0.0.1", port);
            OutputStream stream = socket.getOutputStream();
            String shutdown = "SHUTDOWN";
            for (int i = 0; i < shutdown.length(); i++)
                stream.write(shutdown.charAt(i));
            stream.flush();
            stream.close();
            socket.close();
            System.out.println("The server was successfully shut down.");
        } catch (IOException e) {
            System.out.println("Error. The server has not been started.");
        }
    }
}

结果:正常关闭流程走完,JVM进程即将终止时又触发了关闭钩子,导致 StandardServer#stop() 方法重复执行并报错了。

实验三:发送“shutdown”消息后马上执行 kill 命令

StandardServer#stop() 仍然被执行了两次,导致报错。

实验四:仅发送“shutdown”消息,但是stop方法中加上移除关闭钩子的方法

结果:Tomcat正常关闭,没有报错。可见stop() 方法中移除了关闭钩子后,在终止JVM进程时就不会再触发关闭钩子了。

实验五:stop方法中加上移除关闭钩子的方法,发送“shutdown”消息后马上执行 kill 命令

我来说下现象:发送“shutdown”消息后,stop() 方法开始执行,并移除了关闭钩子。接下来执行  kill 命令,可以看到进程被立刻终止了,并没有等待stop()方法执行完。分析一下执行 kill 命令后的现象,应该是系统发现该进程已经没有关闭钩子了(被刚才的stop方法移除掉了),于是认为这个进程不需要遗言,于是干脆利落的杀死了。

对比实验四的日志也可以发现在结尾少了两条 stop() 方法中的日志。

通过上述五个实验可以得出,在 stop() 方法中移除关闭钩子,可以防止用户守规矩的通过发送“shutdown”消息来关闭Tomcat时,关闭钩子又被触发,stop() 方法被重复执行的问题。但是“shutdown”消息和 kill 命令两种关闭方式最好别一起用,否则很容易达到 kill -9 的效果。

OK,本章的内容就到这里,本章主要介绍了Java中的“关闭钩子”机制,Tomcat运用了这个机制,最大程度的保证了用户在关闭Tomcat时,StandardServer#stop() 方法能够被执行,实现了Tomcat的优雅关闭。下一章我们来研究下Tomcat的启动脚本,敬请期待!

源码分享

https://gitee.com/huo-ming-lu/HowTomcatWorks

本章中自定义关闭钩子的代码在Bootstrap类中

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

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

相关文章

2024年精选推荐的16个向量数据库:提升你的AI应用性能

在人工智能时代&#xff0c;向量数据库已成为数据管理和AI模型不可或缺的一部分。向量数据库是一种专门设计用来存储和查询向量嵌入数据的数据库。这些向量嵌入是AI模型用于识别模式、关联和潜在结构的关键数据表示。随着AI和机器学习应用的普及&#xff0c;这些模型生成的嵌入…

提升工作效率,这些AI工具网址值得收藏

对于希望利用人工智能&#xff08;AI&#xff09;来提升工作效率来说&#xff0c;以下是一些精选的AI工具网址&#xff0c;涵盖了从文案写作、数据分析、邮件自动化到创意设计等多个领域。这些工具旨在帮助你更高效地完成日常工作&#xff0c;节省时间并提高生产力。 Eightify …

光照老化试验箱在化工产品暴晒测试中的应用

概述 光照老化试验箱是一种模拟自然光照条件下材料老化情况的实验设备&#xff0c;广泛应用于化工、建材、电子、汽车等行业中对材料的耐候性、耐光性能等进行测试。通过模拟日光中的紫外线和温度等环境因素&#xff0c;加速材料老化过程&#xff0c;以此评估材料在长期使用中…

6.7、函数的分文件编写

mian函数部分代码 #include <iostream> using namespace std; #include <string> #include "swap.h"//函数的分文件编写 //实现两个数字进行交换的函数//函数的声明 //void swap(int a,int b); //函数的定义 //void swap(int a, int b) //{ // int temp…

C++: 左值引用和右值引用

目录 概念&#xff1a; 理解&#xff1a; 左值引用&#xff0c;右值引用 左值引用能否给右值取别名&#xff1f; 右值引用能否给左值取别名&#xff1f; 引用的意义是什么&#xff1f; 左值和右值对自定义类型有什么区别吗&#xff1f; move的妙用&#xff01; 没有优化…

SpringMVC常见的注解

一、Spring MVC Spring Web MVC是基于ServletAPI构建的原始web 框架&#xff0c;一开始就包含在Spring 框架中&#xff0c;通常被称为“Spring MVC”。 1.MVC 是什么&#xff1f; MVC(Model、View、Controller&#xff09;是软件工程中的一种软件架构设计模型。它把软件系统分…

力扣hot100-普通数组2

文章目录 题目&#xff1a;轮转数组方法1-使用额外的数组方法2-三次反转数组 除自身以外数组的乘积方法1-用到了除法方法2-前后缀乘积法 题目&#xff1a;轮转数组 原题链接&#xff1a;轮转数组 方法1-使用额外的数组 方法1是自己写出来的。方法2参考的别人的&#xff0c;…

transformer模型学习路线_transformer训练用的模型

Transformer学习路线 完全不懂transformer&#xff0c;最近小白来入门一下&#xff0c;下面就是本菜鸟学习路线。Transformer和CNN是两个分支&#xff01;&#xff01;因此要分开学习 Transformer是一个Seq2seq模型&#xff0c;而Seq2seq模型用到了self-attention机制&#xf…

tinyshop项目部署

参考软件测试之测试用例设计&#xff08;四&#xff09;_管理后台 测试用例-CSDN博客 1、下载xampp 2、修改apache和mysql的端口分别为4431 &#xff0c;8013和3306 3、访问页面&#xff1a;输入ip:端口号&#xff0c;出现以下页面即成功 4、安装tinyshop商城 将解压的tinys…

苹果手机数据被抹除了怎么恢复?学会这招就够了

数据丢失&#xff0c;是每一个iOS用户都有可能会遇到的问题&#xff0c;尤其是因为操作不当不小心抹除了数据&#xff0c;这个问题会变得更加糟糕&#xff0c;因为 iPhone中的数据、设置、下载的软件等内容都会被清除掉。那么iphone抹掉数据后怎么恢复&#xff1f;本文提供了相…

VMware虚拟机Ubuntu网络有线线缆已拔出问题

1、问题描述 VMware虚拟机Ubuntu不能联网&#xff0c;打开设置中&#xff0c;网络显示“有线 线缆已拔出”。 2、查看虚拟网络连接 查看主机的网络连接&#xff0c;确保虚拟网络已启用。 3、启动虚拟机网络服务 打开主机的 ‘服务’&#xff08;winr&#xff0c;运行框中输入…

视频监控平台LntonCVS视频监控汇聚平台技术优势分析

安防视频管理系统LntonCVS视频智能汇聚融合平台&#xff0c;是一款功能强大的视频管理平台&#xff0c;支持多种操作系统包括Windows、Linux&#xff08;CentOS、Ubuntu&#xff09;和国产化系统。该平台能够通过多协议接入&#xff0c;并利用视频应用引擎将多种格式的视频数据…

ABAP OO面向对象编程--定义、实现和使用类

- Define, implement and use simple class&#xff08;定义、实现和使用简单类&#xff09;&#xff1a;指的是在编程中创建一个基本的类&#xff0c;实现其功能&#xff0c;并在程序中使用它。 类是对象的模板。反过来说&#xff0c;也可以说对象的类型就是它的类。类是对对象…

聊一聊领域驱动和贫血

写在前面 前段时间跟领导讨论技术债概念时不可避免地提到了代码的质量&#xff0c;而影响代码质量的因素向来都不是单一的&#xff0c;诸如项目因素、管理因素、技术选型、人员素质等等&#xff0c;因为是技术债务&#xff0c;自然就从技术角度来分析&#xff0c;单纯从技术角…

秋招Java后端开发冲刺——非关系型数据库篇(MongoDB)

MongoDB 本文介绍非关系型数据库MongoDB的基础知识和常见面试题。 &#xff08;一&#xff09;基础知识 1. 介绍&#xff1a;MongoDB是一个基于分布式文件存储的数据库&#xff0c;由C语言编写&#xff0c;旨在为WEB应用提供可扩展的高性能数据存储解决方案。 2.特点 特点…

数次同台,极越与小米的爱恨情仇

在汽车行业&#xff0c;提起小米&#xff0c;或许很多人都会记得在北京车展登台亮相的小米su7。可提到极越&#xff0c;或许更多人会为之一愣——这是个什么汽车品牌&#xff1f; 实际上&#xff0c;在今年四月的北京车展中&#xff0c;它就坐落于小米汽车W2展馆正对面。这也可…

Electron、Win11静默打印与PowerShell:技术融合与应用探索

Electron、Win11静默打印与PowerShell&#xff1a;技术融合与应用探索 在现代软件开发与办公环境中&#xff0c;技术的融合与创新不断推动着工作效率的提升和用户体验的优化。本文将深入探讨Electron框架、Windows 11&#xff08;Win11&#xff09;静默打印技术以及PowerShell…

Win11禁止右键菜单折叠的方法

背景 在使用windows11的时候&#xff0c;会发现默认情况下&#xff0c;右键菜单折叠了。以至于在使用一些软件的右键菜单时总是要点击“显示更多选项”菜单展开所有菜单&#xff0c;然后再点击。而且每次在显示菜单时先是全部展示&#xff0c;再隐藏一下&#xff0c;看着着实难…

小试牛刀-区块链代币锁仓(Web页面)

Welcome to Code Blocks blog 本篇文章主要介绍了 [区跨链代币锁仓(Web页面)] ❤博主广交技术好友&#xff0c;喜欢我的文章的可以关注一下❤ 目录 1.编写目的 2.开发环境 3.实现功能 4.代码实现 4.1 必要文件 4.1.1 ABI Json文件(LockerContractABI.json) 4.2 代码详解…

《新华日报》理论版报刊简介及投稿邮箱

《新华日报》理论版报刊简介及投稿邮箱 《新华日报》是中国共产党在抗日战争时期和解放战争初期创办的大型机关报&#xff0c;1949 年 4 月在南京复刊&#xff0c;1952 年成为中国共产党江苏省委机关报&#xff0c;现为中共江苏省委直属事业单位。 该报纸的理论版&#xff08;…