CTRL + C w/Spring Загрузка и Gradle Убивает Gradle Демон

Я использую плагин 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?

Ответы

Ответ 1

Это все еще проблема в Gradle 4. Мой лучший компромисс/решение (построение ответа charlie_pl):

  • Нажмите ctrl+z, чтобы отправить выполняемый процесс на задний план.
  • Убейте процесс следующим образом: kill $(ps aux | grep "MyApp" | grep -v grep | awk '{print $2}')
  • Перезагрузка: ./gradlew run ...

Ответ 2

Я не знаком с плагином Spring, поэтому, по-видимому, нет команды "bootStop" (как есть в плагине Jetty). Кроме того, после расширенного поиска я не думаю, что для желаемого результата есть опция командной строки.

Одним из вариантов, хотя, по общему признанию, является kludge, является использование API-интерфейса Tooling. (Полный пример кода здесь.)

Идея состоит в том, чтобы запустить долговременную задачу в Groovy script. По команде script остановит задачу и вызовет 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 script заключается в том, чтобы запустить задачу в фоновом потоке, а затем отправить маркер отмены после получения команды.

Для ясности я проиллюстрирую два раздела. Первый раздел - фоновый поток:

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)
    }
}

Этот код можно легко настроить для выполнения других задач, для перезапуска задачи и т.д. Он намекает на прославленную оболочку вокруг использования личной командной строки.

Ответ 3

Вот объяснение от основного разработчика, почему Ctrl + C уничтожит демона.

Это всегда был "по дизайну", но мы хотим отойти от него, чтобы демона так часто не убивали. Я думаю, что есть случаи, когда мы не распространяем ctrl + c, но это удача.

Если вы посмотрите, что мы делаем для непрерывного режима в 2.5, мы добавляем ctrl + d для выхода из процесса Gradle, не убивая демона. У нас есть аналогичная проблема с bootRun с нашей поддержкой приложений Play Play (playRun), которая использует тот же механизм (ctrl + d). Я думаю, что в конечном итоге мы сделаем что-то подобное в целом, но нам нужно предоставить альтернативный способ для существующих скриптов сборки читать stdin, прежде чем мы будем захватывать вход все время.

- Стерлинг Грин (Gradle Core Dev)

Ответ 5

У меня была та же проблема. Я начал приложение dropwizard и убил демона, чтобы увеличить время перезапуска приложения.

Простое решение: В конце я просто искал процесс dropwizard и убил его в командной строке (простая комбинация kill и ps aux и grep). Это закрывает приложение и не выполняет сборку, но сохраняет демона.

Ответ 6

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