【问题标题】:CTRL+C w/ Spring Boot & Gradle Kills Gradle DaemonCTRL+C w/ Spring Boot & Gradle 杀死 Gradle 守护进程
【发布时间】:2016-12-31 14:17:09
【问题描述】:

我使用 Spring Boot Gradle 插件来启动 Tomcat 服务器和我的应用程序。我通过gradle bootRun 启动Tomcat 服务器。我还启用了 Gradle 守护程序,希望让 Gradle 构建速度更快。

但是,启用守护程序是徒劳的。每次我通过 Ctrl + C 停止服务器,然后用gradle bootRun 再次启动服务器时,都会遇到以下消息:

Starting a new Gradle Daemon for this build (subsequent builds will be faster).

Ctrl + C 不仅在 Spring Boot 的掩护下停止了 Tomcat 服务器,还杀死了 Gradle 守护进程。这违背了 Gradle 守护程序模式的目的。

有没有更好的方法来停止服务器,希望通过命令行界面在我使用gradle bootRun 启动 tomcat 的同一终端中保持 Gradle 守护进程的活动?

【问题讨论】:

    标签: java tomcat gradle spring-boot


    【解决方案1】:

    这仍然是 Gradle 4 中的一个问题。我最好的妥协/解决方案(建立 charlie_pl 的答案):

    1. ctrl+z将正在运行的进程发送到后台。
    2. 杀死进程如:kill $(ps aux | grep "MyApp" | grep -v grep | awk '{print $2}')
    3. 重启:./gradlew run ...

    【讨论】:

    • 其中"MyApp" 可能是"tomcat",并且步骤#3 应该只是运行fg 以恢复Gradle 进程——或者更好的是,将其附加到步骤#2 的末尾作为&& fg
    • 好吧,就我而言,我使用的是 Dropwizard,所以不是tomcat,但可以肯定。也没有通过fg 带回的过程;我们已经在第 2 步中杀死了它。我将进程发送到后台的唯一原因是我不必去新的终端来终止进程。
    • 我的评论特别是关于在 Gradle 中使用 Spring Boottomcat (如 OP 所述)。如果你不使用 Spring Boot,情况可能会有所不同,但我的直觉是,如果你不打算运行 fg,你还不如只运行 Ctrl-C。顺便说一句,当你运行fg 时,它允许 Spring/Gradle 在返回命令提示符之前进行一些清理。
    【解决方案2】:

    我不熟悉 Spring Boot 插件,所以大概没有“bootStop”命令(就像在 Jetty 插件中一样)。此外,经过广泛搜索后,我认为没有所需结果的命令行选项。

    一个选项,虽然公认是一个杂项,是使用工具 API。 (完整代码示例is here。)

    这个想法是在 Groovy 脚本中启动长时间运行的任务。在命令下,脚本将停止任务并调用gradle tasks 来触发守护进程。

    从上面链接的 GitHub 代码中,一个长时间运行的任务可能是:

    task runService() << {
        ant.delete(file: "runService.log")
        def count = 0
        while(true) {
            new File("runService.log").withWriterAppend {
                it.writeLine("[runService] count: ${count}")
            }
            println "sleeping ...."
            try { Thread.sleep(5 * 1000) } catch (Exception ex) {}
            count++
        }
    }
    

    Groovy 脚本背后的想法是在后台线程中启动任务,然后在收到命令时发送取消令牌。

    为清楚起见,我将说明两个部分。第一段是后台线程:

    class BuildRunner implements Runnable {
        def connector
        def tokenSource
        def taskName
    
        BuildRunner(connector, tokenSource, taskName) {
            this.connector = connector
            this.tokenSource = tokenSource
            this.taskName = taskName
        }
    
        public void run() {
            def connection = connector.connect()        
            try {            
                def build = connection.newBuild()
                build.withCancellationToken(tokenSource.token())
                build.setStandardOutput(System.out)
                build.setStandardError(System.err)
                build.forTasks(taskName)
                build.run()
                println "${taskName} is finishing ..."
            } catch(BuildCancelledException bcex) {
                println "received cancel signal"
                println "tickling daemon ..."
                tickleDaemon(connector)
                println "Done."
                System.exit(0)
            } catch(Exception ex) {
                println "caught exception : " + ex
            } finally {            
              connection.close()        
            }        
        }
    
        def tickleDaemon = { connector ->
            final String TASKS = "tasks"
            def connection = connector.connect()        
            def build = connection.newBuild()
            build.forTasks(TASKS)
            build.run()
        }
    }
    

    另一部分是主控制台:

    // main -----------
    
    // edit as appropriate
    final String TASK_NAME = "runService"
    final String GRADLE_INSTALL_DIR = "/Users/measter/tools/gradle-2.14.1"
    final String PROJECT_DIR = "../service"
    
    def connector = GradleConnector.newConnector()
    connector.useInstallation(new File(GRADLE_INSTALL_DIR))
    connector.forProjectDirectory(new File(PROJECT_DIR))
    
    def tokenSource = connector.newCancellationTokenSource()
    
    println "starting ${TASK_NAME}"
    def buildRunner = new BuildRunner(connector, tokenSource, TASK_NAME)
    new Thread(buildRunner).start()
    
    def console = new Scanner(System.in)
    println "Enter a command (S: stop task, Q: quit): "
    
    while (console.hasNextLine()) {
        def lineTokenizer = new Scanner(console.nextLine())
        String token = lineTokenizer.next()
        if (token.equalsIgnoreCase("S")) {
            tokenSource.cancel()
        } else if (token.equalsIgnoreCase("Q")) {
            println "Done."
            System.exit(0)
        }
    }
    

    可以轻松自定义此代码以执行其他任务、重新启动任务等。它暗示了一个围绕个人命令行使用的美化包装。

    【讨论】:

      【解决方案3】:

      这里是核心开发人员解释为什么 Ctrl + C 会杀死守护进程。

      “设计”一直都是这种方式,但我们想远离它,这样守护程序就不会经常被杀死。我认为在某些情况下我们不传播 ctrl+c,但那是幸运的。

      如果您看看我们在 2.5 中为连续模式所做的事情,我们正在添加 ctrl+d 以退出 Gradle 进程而不杀死守护进程。我们的 Play 应用程序支持 (playRun) 存在与 bootRun 类似的问题,它使用相同的机制 (ctrl+d)。我认为我们最终会做这样的事情,但我们需要为现有的构建脚本提供一种替代方式,以便在我们一直捕获输入之前读取标准输入。

      ——Sterling Greene(Gradle 核心开发)

      【讨论】:

        【解决方案4】:

        看起来这可能已在 3.1 https://docs.gradle.org/current/release-notes#more-resilient-daemon 中得到解决

        【讨论】:

        【解决方案5】:

        bootRunspring-boot-gradle-plugin 的一个便利功能。它允许您在一个命令中执行两个步骤,并且它具有非常小的好处,即在此过程中不生成.jar 文件。它还具有潜在的主要好处......

        如果 devtools 已添加到您的项目中,它将自动 监控您的应用程序的更改。

        如果您没有使用 bootRun 的实时更新功能,您可以通过将构建/运行序列作为两个命令执行来解决此问题,如 "Running your application" 中所述。由于第二个命令不涉及 Gradle,因此您现在可以 Ctrl-C 服务器,而不会使 Gradle 处于 CANCELED 状态。

        以下是应用这种方法的示例:

        gradle build && java -jar build/libs/myproject-0.0.1-SNAPSHOT.jar
        

        另一方面,如果您正在使用devtools,您可能不需要经常手动重新启动服务器——只需重新构建,服务器就会自行重新启动(对于 Groovy,您只需更新源文件)。

        只要类路径上的文件发生变化,使用 spring-boot-devtools 的应用程序就会自动重启。

        ./gradlew build -x test
        

        ...如果您使用的是 IDE(例如“vscode”),它可能会自动编译您的 java 文件,因此只需保存 java 文件就可以间接启动服务器重启。然后Java 在这方面变得和 Groovy 一样无缝

        【讨论】:

          【解决方案6】:

          我遇到了同样的问题。我启动了 dropwizard 应用程序,并且杀死一个守护进程显着增加了重新启动应用程序的时间。

          简单的解决方案: 最后,我只是简单地搜索了 dropwizard 进程,并在命令行中将其杀死(简单的 kill & ps aux & grep 组合)。这会关闭应用程序并导致构建失败,但会保留守护进程。

          【讨论】:

            猜你喜欢
            • 2019-06-08
            • 1970-01-01
            • 1970-01-01
            • 2018-12-21
            • 1970-01-01
            • 1970-01-01
            • 2014-08-30
            • 2013-04-29
            • 1970-01-01
            相关资源
            最近更新 更多