CTRL + C w / Spring Boot&Gradle Kills Gradle Daemon
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CTRL + C w / Spring Boot&Gradle Kills Gradle Daemon相关的知识,希望对你有一定的参考价值。
我使用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守护进程保持活着状态?
这仍然是Gradle 4中的一个问题。我最好的妥协/解决方案(建立charlie_pl的答案):
- 按
ctrl+z
将正在运行的进程发送到后台。 - 杀死这个过程:
kill $(ps aux | grep "MyApp" | grep -v grep | awk '{print $2}')
- 重启:
./gradlew run ...
我不熟悉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)
}
}
可以轻松地自定义此代码以执行其他任务,重新启动任务等。它提示了一个围绕个人命令行使用的美化包装。
以下是核心开发人员解释为什么Ctrl + C会杀死守护进程。
它总是以“按设计”的方式进行,但我们希望远离它,这样守护进程就不会经常被杀死。我认为有些情况下我们不会传播ctrl + c,但那是运气。
如果你看看我们在2.5中为连续模式做了什么,我们将添加ctrl + d来退出Gradle进程而不会杀死守护进程。我们的PlayRun与Play应用程序支持(playRun)有类似的问题,它使用相同的机制(ctrl + d)。我想我们最终会做这样的事情,但是我们需要为现有的构建脚本提供一种替代方法,以便在我们一直捕获输入之前读取stdin。
- Sterling Greene(Gradle Core Dev)
看起来这可能已在3.1 https://docs.gradle.org/current/release-notes#more-resilient-daemon中得到解决
bootRun
是spring-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一样无缝。
我有同样的问题。我开始使用dropwizard应用程序,并且杀死一个守护进程可能会增加重启应用程序的时间。
简单的解决方案:最后我只是搜索了dropwizard进程,并在命令行中杀死了它(简单的kill&ps aux&grep组合)。这会关闭应用程序并使构建失败,但会保留守护程序。
以上是关于CTRL + C w / Spring Boot&Gradle Kills Gradle Daemon的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot 2 Actuator Starter 中的 Nullpointer w。 Tomcat指标
Spring Boot w/o starter-parent pom 不加载 jdbc 驱动程序
如何在 Spring Boot 2(w/WebFlux)中为 HTTP 和 HTTPS 配置两个端口?
Spring Boot web app w/session + CSRF & stateless Basic Auth w/o CSRF