Tomcat 未完全关闭(Tomcat 9.0.31)

Tomcat 未完全关闭(Tomcat 9.0.31)

我们正在将应用程序从 Websphere 迁移到 Tomcat。我注意到的一件事是,当我shutdown.sh为 Tomcat 发出时,该进程仍在运行。最初我以为该进程会在一两分钟内自行结束,但即使 5 分钟后,我仍然看到该进程还在运行。所以我必须使用以下命令终止该进程,kill -9然后重新开始。

关机时我确实看到控制台上出现很多错误,例如:

org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [some_application] appears to have started a thread named [Timer-0] but has failed to stop it.

代码由由 servlet、spring 混合构成的 webapps 组成,应用程序启动时会启动大量线程。

我可以在配置方面做些什么来解决这个问题?或者这需要一些代码工作来弥补 WebSphere 提供的开箱即用功能?

答案1

类似这样的问题最好通过代码来处理。

catalina.sh stop -force尽管无论如何您都可以使用来终止 Tomcat 实例,但这里的重点是 Tomcat 认为这些线程出于某种原因而运行,而终止线程可能会导致应用程序处于不一致的状态(例如,如果在写入某些内容的过程中途终止)。

如果您确实知道不会发生这样的事情,请-force在停止 Tomcat 时使用该开关。

然而,最好在编写应用程序时将线程标记为守护进程(通过调用setDaemon(true)实例Thread),这样当 VM 退出时它们就可以被终止,或者(如果线程不应随时被终止)通过实现来注册一个关闭挂钩ServletContextListener,并在方法中执行关闭工作contextDestroyed这是StackOverflow 上关于安装关闭钩子的问题。

答案2

你的申请可能会留下一堆非守护进程线程正在运行,因此它永远不会关闭。

您在问题中提到的线程是TimerThread,Tomcat 知道如何 (几乎) 安全地关闭它们。只需将其添加clearReferencesStopTimerThreads="true"到您的应用程序上下文中:

<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="yourAppName"
         clearReferencesStopTimerThreads="true" />

但是,正如 Lacek 所提到的,处理它们的正确方法是通过ServletContextListener或其他机制来管理 的Thread生命周期。由于您使用的是 Spring 容器,因此我会添加一个任务调度器<task:scheduler id="" pool-size="" />ApplicationContext并将其注入到每个需要安排任务的 bean 中。如果您的 servlet 需要安排任务,请创建一个ScheduledExecutorService在中ServletContextListener并将对它的引用存储在中ServletContext

PS:还有一个clearReferencesStopThreads属性,但是描述Apache Tomcat 配置手册应该给你一个提示,何时使用它:

如果为 true,Tomcat 将尝试终止由 Web 应用程序启动的线程。停止线程是通过已弃用的(出于充分理由)Thread.stop() 方法执行的,并且可能会导致不稳定。因此,启用此功能应被视为最后一招在开发环境中,不建议在生产环境中使用。如果未指定,将使用默认值 false。如果启用此功能,Web 应用程序可能需要最多两秒钟才能停止,因为在对任何剩余线程调用 Thread.stop() 之前,执行器线程最多有两秒钟的时间正常停止。

相关内容