tomcat 正常关闭_Tomcat的带有守护程序和关闭钩子的正常关闭

tomcat 正常关闭

我的最后两个博客讨论了长时间轮询和Spring的DeferredResult技术,并且为了展示这些概念,我将我的Producer Consumer项目中的代码添加到了Web应用程序中。 尽管该代码演示了博客所提出的观点,但其逻辑上确实包含大量漏洞。 除了在实际的应用程序中不会使用简单的LinkedBlockingQueue而是会选择JMS或其他一些具有工业实力的消息传递服务这一事实,以及只有一个用户可以掌握匹配更新的事实之外,还有一个问题生成行为不佳的线程,这些线程在JVM终止时不会关闭。

您可能想知道为什么这应该是一个问题……对您来说,作为开发人员,这根本不是问题,这只是一点点草率的编程,但是对于您的其中一个操作人员而言,它可能会使生活变得不必要地困难。 这样做的原因是,如果您有太多行为异常的线程,那么键入Tomcat的shutdown.sh命令将几乎没有效果,并且您必须通过键入以下命令来严重杀死Web服务器:

ps -ef | grep java

得到pid然后

kill -9 <<pid>>

…并且当您有一个Tomcat Web服务器字段来重新启动所有这些额外的问题时,这将变得非常痛苦。 当您键入shutdown.sh您希望Tomcat停止。

在我的前两篇博客中,我创建的行为不良的线程具有以下run()方法,其中第一个方法run()如下所示)的行为确实不良:

@Override public void run() { while (true) { try { DeferredResult<Message> result = resultQueue.take(); Message message = queue.take(); result.setResult(message); } catch (InterruptedException e) { throw new UpdateException("Cannot get latest update. " + e.getMessage(), e); } } }

在这段代码中,我使用了一个无限的while(true) ,这意味着线程将一直运行并且永不终止。

@Override public void run() { sleep(5); // Sleep to allow the reset of the app to load logger.info("The match has now started..."); long now = System.currentTimeMillis(); List<Message> matchUpdates = match.getUpdates(); for (Message message : matchUpdates) { delayUntilNextUpdate(now, message.getTime()); logger.info("Add message to queue: {}", message.getMessageText()); queue.add(message); } start = true; // Game over, can restart logger.warn("GAME OVER"); }

上面的第二个示例也表现不佳。 它将继续从MatchUpdates列表中获取消息,并在适当的时候将其添加到消息队列中。 它们唯一的好处是,它们可能会抛出InterruptedException ,如果处理不当,将导致线程终止。 但是,这不能保证。

确实,有一个快速修复程序……您要做的就是确保您创建的任何线程都是守护程序线程。 守护程序线程的定义是一个线程,它不会阻止JVM在程序完成时退出,但该线程仍在运行。 守护程序线程的通常示例是JVM的垃圾回收线程。 要将线程转换为守护程序线程,只需调用:

thread.setDaemon(true);

…然后当您键入shutdown.shWHAM ,所有线程将消失。 但是,这有一个问题。 如果您的守护程序线程中的一个正在做重要的事情并将其砍掉,这会丢失一些非常重要的数据怎么办?

您需要做的是确保所有线程正常关闭,以完成当前可能正在执行的所有工作。 本博客的其余部分演示了针对这些错误线程的修复程序,通过使用ShutdownHook优雅地协调了它们的ShutdownHook 。 根据文档 ,“ shutdown hook”只是一个初始化但未启动的线程。 当虚拟机开始其关闭序列时,它将以某种未指定的顺序启动所有已注册的关闭挂钩,并使其同时运行。” 因此,在阅读了最后一句话之后,您可能已经猜到您需要做的是创建一个线程,该线程负责关闭所有其他线程,并作为关闭钩子传递给JVM。 所有这些都可以在几个小类中通用,并且可以通过对现有线程run()方法执行一些棘手的操作来实现。

要创建的两个类是ShutdownServiceHook 。 我将首先演示的Hook类用于将ShutdownService链接到您的线程。 Hook的代码如下:

public class Hook { private static final Logger logger = LoggerFactory.getLogger(Hook.class); private boolean keepRunning = true; private final Thread thread; Hook(Thread thread) { this.thread = thread; } /** * @return True if the daemon thread is to keep running */ public boolean keepRunning() { return keepRunning; } /** * Tell the client daemon thread to shutdown and wait for it to close gracefully. */ public void shutdown() { keepRunning = false; thread.interrupt(); try { thread.join(); } catch (InterruptedException e) { logger.error("Error shutting down thread with hook", e); } } 
}

Hook包含两个实例变量: keepRunningthreadthread是对该线程的引用,该Hook该实例负责关闭,而keepRunning告诉该线程…继续运行。

Hook有两个公共方法: keepRunning()shutdown() 。 线程调用keepRunning()以确定是否应该继续运行,而ShutdownService的shutdown钩子线程调用shutdown()来使线程关闭。 这是两种方法中最有趣的。 首先,它将keepRunning变量设置为false。 然后,它调用thread.interrupt()来中断线程,迫使其引发InterruptedException 。 最后,它调用thread.join()并等待thread实例关闭。

请注意,此技术依赖于您所有线程的协作。 如果混合中有一个行为不佳的线程,那么整个事情可能会死机。 要解决此问题,请向thread.join(…)添加超时。

@Service 
public class ShutdownService { private static final Logger logger = LoggerFactory.getLogger(ShutdownService.class); private final List<Hook> hooks; public ShutdownService() { logger.debug("Creating shutdown service"); hooks = new ArrayList<Hook>(); createShutdownHook(); } /** * Protected for testing */ @VisibleForTesting protected void createShutdownHook() { ShutdownDaemonHook shutdownHook = new ShutdownDaemonHook(); Runtime.getRuntime().addShutdownHook(shutdownHook); } protected class ShutdownDaemonHook extends Thread { /** * Loop and shutdown all the daemon threads using the hooks * * @see java.lang.Thread#run() */ @Override public void run() { logger.info("Running shutdown sync"); for (Hook hook : hooks) { hook.shutdown(); } } } /** * Create a new instance of the hook class */ public Hook createHook(Thread thread) { thread.setDaemon(true); Hook retVal = new Hook(thread); hooks.add(retVal); return retVal; } @VisibleForTesting List<Hook> getHooks() { return hooks; } 
}

ShutdownService是一个Spring服务,其中包含一个Hook类的列表,并因此通过推断线程负责关闭。 它还包含一个内部类ShutdownDaemonHook ,该类扩展了Thread 。 的一个实例ShutdownDaemonHook的施工过程中创建ShutdownService ,然后通过调用传递给JVM作为关闭挂钩

Runtime.getRuntime().addShutdownHook(shutdownHook);

ShutdownService具有一个公共方法: createHook() 。 该类要做的第一件事是确保传递给它的任何线程都转换为守护程序线程。 然后,它创建一个新的Hook实例,传入线程作为参数,最后将结果存储在列表中并将其返回给调用者。

现在剩下要做的唯一一件事就是将ShutdownService集成到DeferredResultServiceMatchReporter ,这两个类包含行为不良的线程。

@Service("DeferredService") 
public class DeferredResultService implements Runnable { private static final Logger logger = LoggerFactory.getLogger(DeferredResultService.class); private final BlockingQueue<DeferredResult<Message>> resultQueue = new LinkedBlockingQueue<>(); private Thread thread; private volatile boolean start = true; @Autowired private ShutdownService shutdownService; private Hook hook; @Autowired @Qualifier("theQueue") private LinkedBlockingQueue<Message> queue; @Autowired @Qualifier("BillSkyes") private MatchReporter matchReporter; public void subscribe() { logger.info("Starting server"); matchReporter.start(); startThread(); } private void startThread() { if (start) { synchronized (this) { if (start) { start = false; thread = new Thread(this, "Studio Teletype"); hook = shutdownService.createHook(thread); thread.start(); } } } } @Override public void run() { logger.info("DeferredResultService - Thread running"); while (hook.keepRunning()) { try { DeferredResult<Message> result = resultQueue.take(); Message message = queue.take(); result.setResult(message); } catch (InterruptedException e) { System.out.println("Interrupted when waiting for latest update. " + e.getMessage()); } } System.out.println("DeferredResultService - Thread ending"); } public void getUpdate(DeferredResult<Message> result) { resultQueue.add(result); } }

此类的第一个更改是在Shutdown服务实例中自动连线。 接下来要做的是在创建线程之后但在thread.start()之前,使用ShutdownService创建Hook的实例:

thread = new Thread(this, "Studio Teletype"); hook = shutdownService.createHook(thread); thread.start();

最后的更改是将while(true)替换为:

while (hook.keepRunning()) {

…告诉线程何时退出while循环并关闭。

您可能还注意到上面的代码中抛出了一些System.out.println()调用。 这是有原因的,这是因为执行关闭钩子线程的顺序不确定。 请记住,您的类不仅试图优雅地关闭,而且其他子系统也试图关闭。 这意味着我的原始代码logger.info(…)失败, logger.info(…)以下异常:

Exception in thread "Studio Teletype" java.lang.NoClassDefFoundError: org/apache/log4j/spi/ThrowableInformationat org.apache.log4j.spi.LoggingEvent.(LoggingEvent.java:159)at org.apache.log4j.Category.forcedLog(Category.java:391)at org.apache.log4j.Category.log(Category.java:856)at org.slf4j.impl.Log4jLoggerAdapter.info(Log4jLoggerAdapter.java:382)at com.captaindebug.longpoll.service.DeferredResultService.run(DeferredResultService.java:75)at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.ClassNotFoundException: org.apache.log4j.spi.ThrowableInformationat org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)... 6 more

这是因为当我尝试调用记录器时,它已经被卸载。 同样,如文档所述:“ Shutdown hooks在虚拟机生命周期的微妙时间运行,因此应进行防御性编码。 尤其应将它们编写为线程安全的,并尽可能避免死锁。 它们也不应盲目地依赖可能已经注册了自己的关闭钩子的服务,因此可能自己处于关闭过程中。 尝试使用其他基于线程的服务,例如AWT事件调度线程,可能会导致死锁。”

MatchReport类具有一些非常相似的修改。 主要区别在于hook.keepRunning()代码位于run()方法的for循环内。

public class MatchReporter implements Runnable { private static final Logger logger = LoggerFactory.getLogger(MatchReporter.class); private final Match match; private final Queue<Message> queue; private volatile boolean start = true; @Autowired private ShutdownService shutdownService; private Hook hook; public MatchReporter(Match theBigMatch, Queue<Message> queue) { this.match = theBigMatch; this.queue = queue; } /** * Called by Spring after loading the context. Will "kick off" the match... */ public void start() { if (start) { synchronized (this) { if (start) { start = false; logger.info("Starting the Match Reporter..."); String name = match.getName(); Thread thread = new Thread(this, name); hook = shutdownService.createHook(thread); thread.start(); } } } else { logger.warn("Game already in progress"); } } /** * The main run loop */ @Override public void run() { sleep(5); // Sleep to allow the reset of the app to load logger.info("The match has now started..."); long now = System.currentTimeMillis(); List<Message> matchUpdates = match.getUpdates(); for (Message message : matchUpdates) { delayUntilNextUpdate(now, message.getTime()); if (!hook.keepRunning()) { break; } logger.info("Add message to queue: {}", message.getMessageText()); queue.add(message); } start = true; // Game over, can restart logger.warn("GAME OVER"); } private void sleep(int deplay) { try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { logger.info("Sleep interrupted..."); } } private void delayUntilNextUpdate(long now, long messageTime) { while (System.currentTimeMillis() < now + messageTime) { try { Thread.sleep(100); } catch (InterruptedException e) { logger.info("MatchReporter Thread interrupted..."); } } } }

此代码的最终测试是在匹配更新序列中途发出Tomcat shutdown.sh命令。 在JVM终止时,它将从ShutdownDaemonHook类调用shutdown钩子。 当此类的run()方法执行时,它将在整个Hook实例列表中循环,告诉它们关闭各自的线程。 如果在服务器日志文件的末尾添加tail -f (在我的案例中为catalina.out,但Tomcat可能配置为与我不同),则将看到条目痕迹,使服务器正常关闭。

该博客随附的代码可在Github上找到: https : //github.com/roghughe/captaindebug/tree/master/long-poll 。

参考:来自Captain Debug's Blog博客的Tomcat的通过守护程序和Shutdown Hooks进行的Graceful Shutdown和我们的JCG合作伙伴 Roger Hughes。

翻译自: https://www.javacodegeeks.com/2013/10/tomcats-graceful-shutdown-with-daemons-and-shutdown-hooks.html

tomcat 正常关闭

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

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

相关文章

php nginx 域名重定向,Nginx默认虚拟主机、用户认证、域名重定向

Nginx默认虚拟主机定义默认虚拟主机配置文件,在http下面加入include vhost/*.conf在/usr/local/nginx/conf/下创建目录#mkdir vhost/ //创建vhost目录#cd vhost/ //进入目录#vim aaa.com.conf //编辑文件server{listen 80 default_server; // 有这个标记的就是默认虚拟主机serv…

Java:使用SingletonStream获得性能

仅具有一个元素的Java流有时会在应用程序中造成不必要的开销。 了解如何使用SingletonStream对象并为其中某些此类流获得十倍的性能&#xff0c;并了解如何同时简化代码。 背景 Java 8中的Stream库是有史以来Java语言最强大的功能之一。 一旦您开始了解它的多功能性和所产生的…

多元线性回归分析spss结果解读_多元线性回归分析理论详解及SPSS结果分析

当影响因变量的因素是多个时候&#xff0c;这种一个变量同时与多个变量的回归问题就是多元回归&#xff0c;分为&#xff1a;多元线性回归和多元非线性回归。这里直说多元线性回归。对比一元线性回归&#xff1a;1.1多元回归模型&#xff1a;1.2多元回归方程1.3估计的多元回归方…

gilab无法解析php文件,gitlab重新设置域名后就无法访问了。

gitlab-ctl status ,查看是nignx启动不成功。查看gitlab-ctl tail日志&#xff0c;一直报这个错误 &#xff1a;> /var/log/gitlab/nginx/current <2020-01-06_09:12:15.16681 /opt/gitlab/embedded/sbin/nginx: error while loading shared libraries: libutility_mini_…

nginx请求转发被拒绝_nginx反向代理(请求转发-URL匹配规则)

反向代理适用于很多场合&#xff0c;负载均衡是最普遍的用法。nginx 作为目前最流行的web服务器之一&#xff0c;可以很方便地实现反向代理。当在一台主机上部署了多个不同的web服务器&#xff0c;并且需要能在80端口同时访问这些web服务器时&#xff0c;可以使用 nginx 的反向…

php调用pdf2html,php html2pdf

*安装composer运行html2pdf时&#xff0c;readme里面建议按照composer-setup.exe安装过程中出现openssl的问题&#xff0c;在php.ini中开启相应extension即可&#xff0c;路径写绝对路径&#xff0c;否则按默认路径找不到在html2pdf的文件路径下&#xff0c;cmd运行composer in…

electron 打开调试_Electron 应用调试指南

Electron 是一个基于 Node.js 和 Chromium 的开源框架&#xff0c;用于构建桌面应用&#xff0c;开发者可以使用 web 技术(HTML&#xff0c;JavaScript 和 CSS)完成整个应用的开发。许多知名桌面应用基于 Electron 实现&#xff0c;如 VSCode&#xff0c;Slack 和 GitHub Deskt…

java中方法的命名_Java方法中的参数太多,第5部分:方法命名

java中方法的命名在上一篇文章 &#xff08;有关处理Java方法中过多参数的系列文章的 第4部分 &#xff09;中&#xff0c;我将方法重载视为一种向客户提供需要较少参数的版本的方法或构造函数的方法。 我描述了该方法的一些缺点&#xff0c;并建议从方法重载中摆脱出来以使用不…

oracle lms进程 内存,Oracle RAC 内存融合(Cache Fusion)

原标题&#xff1a;Oracle RAC 内存融合(Cache Fusion)内存融合核心进程LMS(Global Cache Service Process)&#xff1a;这个进程负责完成GCS的大部分工作&#xff0c;它会维护GRD中数据块资源的信息&#xff0c;完成数据块在实例之间的传递工作&#xff0c;相关消息的发送和接…

sougou ubuntu 优麒麟_搜狗输入法 Linux – V2.3 版发布,完美适配优麒麟 19.10

搜狗输入法 Linux – V2.3 版发布&#xff0c;完美适配优麒麟 19.10fenshezhuiyi2 2019年10月19日 暂无评论 阅读 4,346 次昨天已发布优麒麟操作系统 19.10 版本&#xff0c;该版本集成了全新的控制面板和软件商店&#xff0c;不知道大家是否已下载体验。今天小编要为大家介绍一…

oracle多条sql语句常量,如何在Oracle中一次执行多条sql语句

有时我们需要一次性执行多条sql语句&#xff0c;而用来更新的sql是根据实际情况用代码拼出来的解决方案是把sql拼成下面这种形式&#xff1a;beginupdate TB_VG set seq 1, vessel_id Jin14, vessel_type TRACK where batch_number 20837 and train_id 0233086;update TB_…

JDK 11:新的默认收集方法toArray(IntFunction)

“ JDK 11 Early-Access发行说明 ”表明JDK 11的Early Access Build 20在Collection接口上包括一个新的默认方法 &#xff0c;该方法 “允许将集合的元素转移到所需运行时类型的新创建的数组中”。 这个新的默认方法 [ Collection.toArray&#xff08;IntFunction&#xff09; …

获取清空textarea的文字内容_运用|你会做 词云图(文字云) 吗?

词云图,也叫文字云,是对文本中出现频率较高的“关键词”予以视觉化的展现,词云图过滤掉大量的低频低质的文本信息,使得浏览者只要一眼扫过文本就可领略文本的主旨。今天&#xff0c;兰色就分享一下词云图的做法。制作步骤&#xff1a;1、打开词云图网站打开网页https://wordart…

oracle today函数,oracle日期函数集锦

一、 常用日期数据格式1.Y或YY或YYY 年的最后一位&#xff0c;两位或三位SQL> Select to_char(sysdate,Y) from dual;TO_CHAR(SYSDATE,Y)--------------------7SQL> Select to_char(sysdate,YY) from dual;TO_CHAR(SYSDATE,YY)---------------------07SQL> Select to_…

kali查看共享 linux_在Linux下访问Windows共享文件夹

在Linux下访问Windows共享文件夹说明以下操作以Ubuntu为例&#xff0c;大家可以参考。我在Ubuntu 14.04和16.04都试过了。Windows共享文件夹如果局域网内有一台Windows主机&#xff0c;将指定文件夹设为共享&#xff0c;就可以在局域网内访问了。如果要从Linux下访问该如何操作…

oracle 11gdata guard,Oracle 11g Data Guard配置

操作环境说明&#xff1a;两台服务器使用相同的Redhat 5.5内核版本为&#xff1a;2.6.18-194.el5在主库(primary database)中提前安装好了Oracle 11gR2软件&#xff0c;通过DBCA创建了数据库&#xff0c;实例名为PRIMARY备库(standby database)中只安装了Oracle 11gR2软件&…

java 可变参数方法_Java方法中的参数太多,第7部分:可变状态

java 可变参数方法在我的系列文章的第七篇中&#xff0c;有关解决Java方法或构造函数中过多参数的问题 &#xff0c;我着眼于使用状态来减少传递参数的需要。 我等到本系列的第七篇文章来解决这个问题的原因之一是&#xff0c;它是我最不喜欢的减少传递给方法和构造函数的参数的…

联想笔记本e480恢复出厂设置_联想e480进入bios设置_thinkpade480进入bios的方法

ThinkPad E480笔记本win10改win7如何修改BIOS设置?想要将预装win10系统换成win7系统&#xff0c;最重要的一步就是修改bios设置&#xff0c;这样才能在装机过程中不受到报错困扰&#xff0c;所以今天快启动小编为大家分享了详细图文教程&#xff0c;一起来看看吧。ThinkPad E4…

oracle一页显示15行,oracle rownum分页与显示记录小测

同事问及关于rownum表记录不显示问题,经查阅官方手册,附上测试笔记:SQL> insert into t_rownum select level from dual connect by level<5;5 rows insertedSQL> commit;Commit completeSQL> select * from t_rownum;A---------------------------------------123…

自动化用户特定实体的访问控制

实际上&#xff0c;每个Web应用程序都应该有多个用户&#xff0c;每个用户都有一些数据-帖子&#xff0c;文档&#xff0c;消息等等。 最明显的事情是保护这些实体免遭非这些资源合法所有者的用户获取。 不幸的是&#xff0c;这不是最容易的事情。 我并不是说很难&#xff0c;…