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

时间:2016-08-24 12:31:34

标签: java tomcat gradle spring-boot

我使用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守护程序保持活动状态?

6 个答案:

答案 0 :(得分:5)

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

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

答案 1 :(得分: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 :(得分:2)

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

  

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

     

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

- Sterling Greene(Gradle Core Dev)

答案 3 :(得分:1)

答案 4 :(得分:1)

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

  

如果已将devtools添加到您的项目中,它将自动生成   监控您的申请变更。

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

以下是应用此类方法的示例:

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

另一方面,如果 使用"Running your application",您可能不需要经常手动重启服务器 - 只需重建并且服务器将自行重启(对于Groovy,您只需要需要更新源文件)。

  

使用spring-boot-devtools的应用程序将在类路径上的文件发生更改时自动重新启动。

./gradlew build -x test

...如果您正在使用IDE(例如&#39; vscode&#39;),它可能会自动编译您的java文件,因此只需保存一个java文件即可间接启动服务器重启。然后 Java在这方面就像Groovy一样无缝

答案 5 :(得分:0)

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

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