Java 作为windows服务运行的嵌入式tomcat需要很长时间才能停止服务
我有一个使用嵌入式tomcat(9.0.44)的可执行jar文件。并使用apacheJava 作为windows服务运行的嵌入式tomcat需要很长时间才能停止服务,java,java-11,tomcat9,procrun,java-16,Java,Java 11,Tomcat9,Procrun,Java 16,我有一个使用嵌入式tomcat(9.0.44)的可执行jar文件。并使用apacheprunsrv实用程序以windows服务(名为“MyApp测试服务”)运行。 但当我试图停止服务时,需要一些时间(超过一分钟)才能停止服务。但启动这项服务相当快。 我可以确认tomcat的stop()方法很快完成。我怀疑prunsrv中还有其他东西等待并需要时间来停止服务。请帮助了解发生了什么以及如何解决此问题(执行tomcat.stop()后立即停止服务) 正在注册服务-C:\servicestest\pr
prunsrv
实用程序以windows服务(名为“MyApp测试服务”)运行。
但当我试图停止服务时,需要一些时间(超过一分钟)才能停止服务。但启动这项服务相当快。
我可以确认tomcat的stop()
方法很快完成。我怀疑prunsrv
中还有其他东西等待并需要时间来停止服务。请帮助了解发生了什么以及如何解决此问题(执行tomcat.stop()
后立即停止服务)
- 正在注册服务-
C:\servicestest\prunsrv.exe”“//RS//MyApp测试服务”
- 启动类和方法:
com.samples.myapp.TestEmbeddedServer::main
- 关机类和方法:
com.samples.myapp.TestEmbeddedServer::stop
public class TomcatEmbeddedServer {
private int port;
private Tomcat tomcat;
private Context context;
private static final Logger LOGGER = LoggerFactory.getLogger(TomcatEmbeddedServer.class);
public TomcatEmbeddedServer(int port, String contextName, String tomcatPath, String webAppPath) {
System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
System.setProperty("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "true");
this.port = port;
LOGGER.info("Port: {}", port);
LOGGER.info("Context: {}", contextName);
LOGGER.info("Tomcat Path: {}", tomcatPath);
LOGGER.info("Webapp Path: {}", webAppPath);
// Prepare tomcat server
this.tomcat = new Tomcat();
this.tomcat.setBaseDir(tomcatPath);
// Set up the context
this.context = this.tomcat.addWebapp(contextName, webAppPath);
this.context.setParentClassLoader(Thread.currentThread().getContextClassLoader());
}
public void start() throws LifecycleException {
Connector connector = this.tomcat.getConnector();
connector.setEncodedSolidusHandling(EncodedSolidusHandling.DECODE.getValue());
connector.setPort(this.port);
LOGGER.info("Starting tomcat server ...{}", this.tomcat);
this.tomcat.start();
this.tomcat.getServer().await();
}
public void stop() throws LifecycleException {
this.tomcat.stop();
}
}
public class TestEmbeddedServer {
private static TomcatEmbeddedServer tomcat;
private static Logger log = LoggerFactory.getLogger(TestEmbeddedServer.class);
public static void main(String[] args) {
String tomcatDir = "D:/testserver/tomcat";
String webAppDir = "D:/testserver/mysampleapp";
String context = "/sampleapp";
int port = 8090;
try
{
tomcat = new TomcatEmbeddedServer(port, context, tomcatDir, webAppDir);
tomcat.start();
} catch (Exception e) {
log.error("Failed to start tomcat server." , e);
}
}
public static void stop(String[] args) {
try {
tomcat.stop();
} catch (LifecycleException e) {
log.error("Failed to stop tomcat.", e);
}
}
}
TestEmbeddedServer.java
public class TomcatEmbeddedServer {
private int port;
private Tomcat tomcat;
private Context context;
private static final Logger LOGGER = LoggerFactory.getLogger(TomcatEmbeddedServer.class);
public TomcatEmbeddedServer(int port, String contextName, String tomcatPath, String webAppPath) {
System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true");
System.setProperty("org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH", "true");
this.port = port;
LOGGER.info("Port: {}", port);
LOGGER.info("Context: {}", contextName);
LOGGER.info("Tomcat Path: {}", tomcatPath);
LOGGER.info("Webapp Path: {}", webAppPath);
// Prepare tomcat server
this.tomcat = new Tomcat();
this.tomcat.setBaseDir(tomcatPath);
// Set up the context
this.context = this.tomcat.addWebapp(contextName, webAppPath);
this.context.setParentClassLoader(Thread.currentThread().getContextClassLoader());
}
public void start() throws LifecycleException {
Connector connector = this.tomcat.getConnector();
connector.setEncodedSolidusHandling(EncodedSolidusHandling.DECODE.getValue());
connector.setPort(this.port);
LOGGER.info("Starting tomcat server ...{}", this.tomcat);
this.tomcat.start();
this.tomcat.getServer().await();
}
public void stop() throws LifecycleException {
this.tomcat.stop();
}
}
public class TestEmbeddedServer {
private static TomcatEmbeddedServer tomcat;
private static Logger log = LoggerFactory.getLogger(TestEmbeddedServer.class);
public static void main(String[] args) {
String tomcatDir = "D:/testserver/tomcat";
String webAppDir = "D:/testserver/mysampleapp";
String context = "/sampleapp";
int port = 8090;
try
{
tomcat = new TomcatEmbeddedServer(port, context, tomcatDir, webAppDir);
tomcat.start();
} catch (Exception e) {
log.error("Failed to start tomcat server." , e);
}
}
public static void stop(String[] args) {
try {
tomcat.stop();
} catch (LifecycleException e) {
log.error("Failed to stop tomcat.", e);
}
}
}
下面是我获得的prunserv启动/关闭日志
[2021-06-02 10:42:36] [debug] ( prunsrv.c:1729) [85696] Commons Daemon procrun log initialized
[2021-06-02 10:42:36] [info] ( prunsrv.c:1733) [85696] Commons Daemon procrun (1.1.0.0 64-bit) started
[2021-06-02 10:42:36] [info] ( prunsrv.c:1643) [85696] Running 'MyApp Test Service' Service...
[2021-06-02 10:42:36] [debug] ( prunsrv.c:1417) [85736] Inside ServiceMain...
[2021-06-02 10:42:36] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 2, dwWin32ExitCode = 0, dwWaitHint = 3000, dwServiceSpecificExitCode = 0
[2021-06-02 10:42:36] [info] ( prunsrv.c:1175) [85736] Starting service...
[2021-06-02 10:42:36] [debug] ( javajni.c:236 ) [85736] loading jvm 'C:\Program Files\Java\bin\server\jvm.dll'
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[0] -Dfile.encoding=UTF8
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[1] --add-opens=java.base/java.lang=ALL-UNNAMED
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[2] --add-opens=java.base/java.io=ALL-UNNAMED
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[3] --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[4] --illegal-access=permit
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[5] exit
[2021-06-02 10:42:36] [debug] ( javajni.c:753 ) [85744] Jvm Option[6] abort
[2021-06-02 10:42:39] [debug] ( javajni.c:990 ) [85744] Java Worker thread started com/samples/myapp/TestEmbeddedServer:main
[2021-06-02 10:42:40] [debug] ( prunsrv.c:1235) [85736] Java started com/samples/myapp/TestEmbeddedServer
[2021-06-02 10:42:40] [info] ( prunsrv.c:1333) [85736] Service started in 4102 ms.
[2021-06-02 10:42:40] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 4, dwWin32ExitCode = 0, dwWaitHint = 0, dwServiceSpecificExitCode = 0
[2021-06-02 10:42:40] [debug] ( prunsrv.c:1572) [85736] Waiting for worker to finish...
[2021-06-02 10:43:38] [debug] ( prunsrv.c:885 ) [85696] reportServiceStatusE: dwCurrentState = 3, dwWin32ExitCode = 0, dwWaitHint = 3000, dwServiceSpecificExitCode = 0
[2021-06-02 10:43:39] [info] ( prunsrv.c:984 ) [85296] Stopping service...
[2021-06-02 10:43:40] [debug] ( javajni.c:990 ) [85488] Java Worker thread started com/samples/myapp/TestEmbeddedServer:stop
[2021-06-02 10:43:41] [debug] ( prunsrv.c:1032) [85296] Waiting for Java JNI stop worker to finish...
[2021-06-02 10:43:48] [debug] ( javajni.c:1013) [85744] Java Worker thread finished com/samples/myapp/TestEmbeddedServer:main with status = 0
[2021-06-02 10:43:48] [debug] ( javajni.c:1013) [85488] Java Worker thread finished com/samples/myapp/TestEmbeddedServer:stop with status = 0
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1034) [85296] Java JNI stop worker finished.
[2021-06-02 10:43:48] [debug] ( prunsrv.c:885 ) [85296] reportServiceStatusE: dwCurrentState = 3, dwWin32ExitCode = 0, dwWaitHint = 5000, dwServiceSpecificExitCode = 0
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1141) [85296] Waiting for worker to die naturally...
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1152) [85296] Worker finished gracefully in 0 ms.
[2021-06-02 10:43:48] [info] ( prunsrv.c:1162) [85296] Service stop thread completed.
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1577) [85736] Worker finished.
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1586) [85736] Waiting for ShutdownEvent
[2021-06-02 10:43:48] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 3, dwWin32ExitCode = 0, dwWaitHint = 60000, dwServiceSpecificExitCode = 0
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1589) [85736] ShutdownEvent signaled
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1594) [85736] Waiting 1 minute for all threads to exit
[2021-06-02 10:43:48] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 3, dwWin32ExitCode = 0, dwWaitHint = 60000, dwServiceSpecificExitCode = 0
[2021-06-02 10:44:48] [debug] ( prunsrv.c:1607) [85736] JVM destroyed.
[2021-06-02 10:44:48] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 1, dwWin32ExitCode = 0, dwWaitHint = 0, dwServiceSpecificExitCode = 0
[2021-06-02 10:44:48] [info] ( prunsrv.c:1645) [85696] Run service finished.
[2021-06-02 10:44:48] [info] ( prunsrv.c:1814) [85696] Commons Daemon procrun finished
从关闭信号发出后的这些日志中可以看出,销毁JVM的延迟正好为1分钟,我认为这就是它挂起的地方
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1589) [85736] ShutdownEvent signaled
[2021-06-02 10:43:48] [debug] ( prunsrv.c:1594) [85736] Waiting 1 minute for all threads to exit
[2021-06-02 10:43:48] [debug] ( prunsrv.c:885 ) [85736] reportServiceStatusE: dwCurrentState = 3, dwWin32ExitCode = 0, dwWaitHint = 60000, dwServiceSpecificExitCode = 0
[2021-06-02 10:44:48] [debug] ( prunsrv.c:1607) [85736] JVM destroyed.
在prunmngr UI中,进度指示器被取消,但“开始”按钮未启用。请参见下面的屏幕截图
编辑:通过改变Tomcat的版本获得结果
使用Tomcat版本
停下来的时间
ApacheTomcat/8.5.66
约9秒
ApacheTomcat/9.0.1
约9秒
ApacheTomcat/9.0.10
约9秒
ApacheTomcat/9.0.13
约9秒
ApacheTomcat/9.0.14
约1分钟3秒
ApacheTomcat/9.0.16
约1分钟3秒
ApacheTomcat/9.0.20
约1分钟3秒
ApacheTomcat/9.0.30
约1分钟3秒
ApacheTomcat/9.0.40
约1分钟3秒
ApacheTomcat/9.0.44
约1分钟3秒
ApacheTomcat/9.0.46
约1分钟3秒
ApacheTomcat/10.0.6
约1分钟3秒
自Tomcat版本以来,引入了实用程序执行器:
向服务器添加计划执行器,该执行器可用于处理定期实用程序任务。默认情况下,实用程序线程为非守护程序。(remm)
它的线程故意是非守护进程,因此服务器stop()
不会关闭JVM。要完全停止服务器,必须使用destroy()
:
public void stop()引发LifecycleException{
this.tomcat.stop();
this.tomcat.destroy();
}
问题一定出在您的应用程序中(可能它启动了一些非守护进程线程,但在关机过程中没有停止它们)。您是否尝试使用空应用程序?@PiotrP.Karwasz这似乎是tomcat的问题。我尝试了不同版本的tomcat,发现问题是从tomcat 9.0.14开始出现的。对于9.0.14之前的版本,它会很快关闭(只需约9秒).我已经在有效的问题上添加了我的结果!!现在我可以不用等待一分钟就停止服务了。