CTRL+C с Spring Boot и Gradle убивает Gradle Daemon
Я использую плагин Spring Boot Gradle для запуска сервера Tomcat и моего приложения. Я запускаю сервер Tomcat через gradle bootRun
, Я также включил демон Gradle в надежде ускорить сборку Gradle.
Тем не менее, включение демона ничего не значит. Каждый раз, когда я останавливаю сервер с помощью Ctrl + C, затем снова запускаю сервер с gradle bootRun
Я сталкиваюсь с сообщением:
Starting a new Gradle Daemon for this build (subsequent builds will be faster).
Ctrl + C не только останавливает сервер Tomcat под прикрытием Spring Boot, но также убивает демона Gradle. Который побеждает цель режима демона Gradle.
Есть ли лучший способ остановить сервер, надеюсь, через интерфейс командной строки в том же терминале, для которого я запустил tomcat? gradle bootRun
, что поддерживает демона Gradle?
6 ответов
Это все еще проблема в Gradle 4. Мой лучший компромисс / решение (построение ответа charlie_pl):
- Нажмите
ctrl+z
отправить запущенный процесс в фоновый режим. - Убить процесс как:
kill $(ps aux | grep "MyApp" | grep -v grep | awk '{print $2}')
- Запустить снова:
./gradlew run ...
Я не знаком с плагином Spring Boot, поэтому предположительно нет команды "bootStop" (как в плагине Jetty). Кроме того, после тщательного поиска, я не думаю, что есть опция командной строки для желаемого результата.
Один из вариантов, хотя и по общему признанию, это использование Tooling 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)
}
}
Этот код можно легко настроить для выполнения других задач, перезапуска задачи и т. Д. Он намекает на прославленную оболочку вокруг использования личной командной строки.
Вот объяснение от основного разработчика, почему Ctrl + C убьет демона.
Так было всегда "по замыслу", но мы хотим отойти от него, чтобы демон не убивали так часто. Я думаю, что есть случаи, когда мы не распространяем ctrl+ c, но это случайно.
Если вы посмотрите на то, что мы делаем для непрерывного режима в 2.5, мы добавим Ctrl+ D, чтобы выйти из процесса Gradle, не убивая демона. У нас есть проблема, похожая на bootRun, с нашей поддержкой приложений Play (playRun), которая использует тот же механизм (ctrl+d). Я думаю, что в конечном итоге мы сделаем что-то подобное в общем, но нам нужно предоставить альтернативный способ для существующих сценариев сборки читать stdin, прежде чем мы будем постоянно фиксировать ввод.
- Стерлинг Грин (разработчик Gradle Core Dev)
bootRun
это удобная особенность spring-boot-gradle-plugin
, Это позволяет вам выполнить два шага в одной команде, и имеет очень незначительное преимущество - не генерировать .jar
файл в процессе. Это также имеет потенциально большую выгоду...
Если devtools был добавлен в ваш проект, он будет автоматически следить за изменениями в вашем приложении.
Если вы не используете функции живого обновления bootRun
Вы можете решить эту проблему, выполнив последовательность сборки / запуска в виде двух команд, как описано в разделе "Запуск приложения". Поскольку вторая команда не включает 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 в этом отношении.
Похоже, это могло быть решено в 3.1 https://docs.gradle.org/current/release-notes
У меня такая же проблема. Я запустил приложение dropwizard, и убийство демона значительно увеличило время перезапуска приложения.
Простое решение: в конце концов я просто искал процесс dropwizard и уничтожил его в командной строке (простая комбинация kill & ps aux & grep). Это закрывает приложение и дает сбой при сборке, но сохраняет демон.