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

2022-09-03 07:18:41

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

但是,启用守护程序是徒劳的。每次我通过+停止服务器,然后再次启动服务器,我遇到这样的消息:CtrlCgradle bootRun

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

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

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


答案 1

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

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

答案 2

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

一种选择,虽然诚然是一个笨拙,是使用工具API。(此处提供了完整的代码示例。

这个想法是在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)
    }
}

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


推荐