作为windows服务运行的嵌入式tomcat需要很长时间才能停止服务

问题描述 投票:0回答:2

我有一个使用嵌入式 tomcat(9.0.44) 的可执行 jar 文件。它使用 apache prunsrv 实用程序作为 Windows 服务(名为“

MyApp Test Service
”)运行。 但是当我尝试停止服务时,需要一些时间(超过一分钟)才能停止服务。但启动服务非常快。 我可以确认tomcat的
stop()
方法很快就完成了。我怀疑
prunsrv
中还有其他东西正在等待并需要时间来停止服务。请帮助了解发生了什么以及如何解决这个问题(执行后立即停止服务
tomcat.stop()

  • 注册服务 -
    C:\ServiceTest\prunsrv.exe" "//RS//MyApp Test Service"
  • 启动类和方法:
    com.samples.myapp.TestEmbeddedServer::main
  • 关闭类和方法:
    com.samples.myapp.TestEmbeddedServer::stop

Shutdown & Startup tags in the prunmgr UI

TomcatEmbeddedServer.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();
    }
}

TestEmbeddedServer.java

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

从这些日志中可以看出,在关闭信号之后,正好有 1 分钟的延迟来破坏 JVM。我认为这就是它挂的地方。

[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.

在 prunngr UI 中,进度指示器消失,但启动按钮未启用。请参阅下面的屏幕截图。

GUI after the stop service.. progress bar dismissed but button not enabled

编辑:改变 Tomcat 版本的结果

使用的Tomcat版本 停止所需时间
阿帕奇汤姆猫/8.5.66 ~9秒
Apache Tomcat/9.0.1 ~9秒
Apache Tomcat/9.0.10 ~9秒
Apache Tomcat/9.0.13 ~9秒
Apache Tomcat/9.0.14 ~1分3秒
Apache Tomcat/9.0.16 ~1分3秒
Apache Tomcat/9.0.20 ~1分3秒
Apache Tomcat/9.0.30 ~1分3秒
Apache Tomcat/9.0.40 ~1分3秒
阿帕奇汤姆猫/9.0.44 ~1分3秒
阿帕奇汤姆猫/9.0.46 ~1分3秒
阿帕奇汤姆猫/10.0.6 ~1分3秒
java java-11 tomcat9 procrun java-16
2个回答
2
投票

自 Tomcat 版本9.0.14开始,引入了实用程序执行器:

向服务器添加计划执行器,可用于处理周期性实用任务。默认情况下,实用程序线程是非守护程序线程。 (雷姆)

它的线程故意是非守护进程,以便服务器

stop()
不会关闭 JVM。要完全停止服务器,您必须使用
destroy()
:

    public void stop() throws LifecycleException {
        this.tomcat.stop();
        this.tomcat.destroy();
    }

0
投票

即使在 Tomcat 9.0.75 上我仍然遇到这个问题

就我而言,在迁移到 Tomcat 9 之前我们没有遇到此问题,因此我对 Apache Tomcat 服务的关闭配置进行了一些更改,似乎将时间从 60 秒以上减少到了 20 秒以下。

我添加了 1 秒的超时并添加了 destroy 作为方法

Apache shutdown configuration with destroy method call

© www.soinside.com 2019 - 2024. All rights reserved.