CTRL + C w / Spring Boot&Gradle Kills Gradle Daemon

问题描述 投票:15回答:6

我使用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
6个回答
5
投票

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

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

4
投票

我不熟悉Spring Boot插件,所以可能没有'bootStop'命令(就像Jetty插件中那样)。此外,经过广泛搜索后,我认为没有一个命令行选项可以获得所需的结果。

一种选择,虽然无可否认是kludge,但是使用Tooling 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)
    }
}

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


2
投票

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

它总是以“按设计”的方式进行,但我们希望远离它,这样守护进程就不会经常被杀死。我认为有些情况下我们不会传播ctrl + c,但那是运气。

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

- Sterling Greene(Gradle Core Dev)


1
投票

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


1
投票

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一样无缝。


0
投票

我有同样的问题。我开始使用dropwizard应用程序,并且杀死一个守护进程可能会增加重启应用程序的时间。

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

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