Pregunta ¿Cómo cerrar una aplicación Spring Boot de forma correcta?


En el Documento de arranque de Spring, dijeron que 'Cada SpringApplication registrará un gancho de cierre con la JVM para garantizar que ApplicationContext se cierra correctamente al salir.'

Cuando hago clic ctrl+c en el comando de shell, la aplicación se puede cerrar con gracia. Si ejecuto la aplicación en una máquina de producción, tengo que usar el comando java -jar ProApplicaton.jar. Pero no puedo cerrar el terminal de shell, de lo contrario cerrará el proceso.

Si ejecuto comando como nohup java -jar ProApplicaton.jar &No puedo usar ctrl+c para apagarlo con gracia.

¿Cuál es la forma correcta de iniciar y detener una aplicación Spring Boot en el entorno de producción?


75
2017-10-24 12:05


origen


Respuestas:


Si está utilizando el módulo de actuador, puede cerrar la aplicación a través de JMX o HTTP si el punto final está habilitado (agregar endpoints.shutdown.enabled=true para usted application.properties archivo).

/shutdown - Permite que la aplicación se cierre correctamente (no está habilitada de forma predeterminada).

Dependiendo de cómo se expone un punto final, el parámetro sensible se puede usar como una pista de seguridad. Por ejemplo, los puntos finales sensibles requerirán un nombre de usuario / contraseña cuando se acceda a ellos a través de HTTP (o simplemente deshabilitado si la seguridad web no está habilitada).

Desde el Documentación de arranque de primavera


45
2017-10-25 14:14



En cuanto a la respuesta de @Jean-Philippe Bond,

Aquí hay un ejemplo rápido de maven para que el usuario maven configure el punto final HTTP para apagar una aplicación web de arranque de primavera usando spring-boot-starter-actuator para que pueda copiar y pegar:

1.Maven pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2.application.properties:

#No auth  protected 
endpoints.shutdown.sensitive=false

#Enable shutdown endpoint
endpoints.shutdown.enabled=true

Todos los puntos finales están listados aquí:

3. Enviar un método de publicación para cerrar la aplicación:

curl -X POST localhost:port/shutdown

Nota de seguridad:

si necesita el método de apagado auth protected, también puede necesitar

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-security</artifactId>
</dependency>

configurar detalles:


40
2018-03-29 05:00



Puede hacer que la aplicación Springboot escriba el PID en un archivo y puede usar el archivo pid para detener o reiniciar o obtener el estado usando un script bash. Para escribir el PID en un archivo, registre un oyente en SpringApplication usando ApplicationPidFileWriter como se muestra a continuación:

SpringApplication application = new SpringApplication(Application.class);
application.addListeners(new ApplicationPidFileWriter("./bin/app.pid"));
application.run();

Luego escribe un script bash para ejecutar la aplicación de inicio de primavera. Referencia.

Ahora puede usar la secuencia de comandos para iniciar, detener o reiniciar.


24
2018-01-19 11:05



Aquí hay otra opción que no requiere que cambie el código ni exhiba un punto final de apagado. Cree los siguientes scripts y úselos para iniciar y detener su aplicación.

start.sh

#!/bin/bash
java -jar myapp.jar & echo $! > ./pid.file &

Inicia su aplicación y guarda la identificación del proceso en un archivo

stop.sh

#!/bin/bash
kill $(cat ./pid.file)

Detiene tu aplicación usando la ID del proceso guardado

start_silent.sh

#!/bin/bash
nohup ./start.sh > foo.out 2> foo.err < /dev/null &

Si necesita iniciar la aplicación utilizando ssh desde una máquina remota o una interconexión de CI, entonces use esta secuencia de comandos para iniciar su aplicación. Usando start.sh directamente puede dejar el shell para colgar.

Después de ej. Al volver a implementar su aplicación, puede reiniciarla usando:

sshpass -p password ssh -oStrictHostKeyChecking=no userName@www.domain.com 'cd /home/user/pathToApp; ./stop.sh; ./silent_start.sh'

19
2018-06-16 13:11



Spring Boot proporcionó varios escuchas de aplicaciones mientras intentaba crear el contexto de la aplicación, uno de ellos es ApplicationFailedEvent. Podemos utilizar para saber si el contexto de la aplicación se inicializó o no.

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.context.event.ApplicationFailedEvent; 
    import org.springframework.context.ApplicationListener;

    public class ApplicationErrorListener implements 
                    ApplicationListener<ApplicationFailedEvent> {

        private static final Logger LOGGER = 
        LoggerFactory.getLogger(ApplicationErrorListener.class);

        @Override
        public void onApplicationEvent(ApplicationFailedEvent event) {
           if (event.getException() != null) {
                LOGGER.info("!!!!!!Looks like something not working as 
                                expected so stoping application.!!!!!!");
                         event.getApplicationContext().close();
                  System.exit(-1);
           } 
        }
    }

Agregar a la clase de escucha anterior a SpringApplication.

    new SpringApplicationBuilder(Application.class)
            .listeners(new ApplicationErrorListener())
            .run(args);  

3
2017-09-01 11:52



A partir de Spring Boot 1.5, no existe un mecanismo de apagado elegante fuera de la caja. Algunos arrancadores de arranque de primavera proporcionan esta funcionalidad:

  1. https://github.com/jihor/hiatus-spring-boot
  2. https://github.com/gesellix/graceful-shutdown-spring-boot
  3. https://github.com/corentin59/spring-boot-graceful-shutdown

Soy el autor de nr. 1. El motor de arranque se llama "Hiatus for Spring Boot". Funciona en el nivel del equilibrador de carga, es decir, simplemente marca el servicio como OUT_OF_SERVICE, sin interferir con el contexto de la aplicación de ninguna manera. Esto permite un cierre elegante y significa que, si es necesario, el servicio puede dejar de funcionar por un tiempo y luego volver a la vida. La desventaja es que no detiene la JVM, tendrás que hacerlo con kill mando. Como manejo todo en contenedores, esto no fue gran cosa para mí, porque de todos modos tendré que detenerme y retirar el contenedor.

Los números 2 y 3 están más o menos basados ​​en esta publicación por Andy Wilkinson. Funcionan en una sola dirección: una vez que se activan, eventualmente cierran el contexto.


2
2017-09-29 13:29



SpringApplication registra implícitamente un enlace de cierre con la JVM para garantizar que ApplicationContext se cierra correctamente al salir. Eso también llamará a todos los métodos de bean anotados con @PreDestroy. Eso significa que no tenemos que usar explícitamente el registerShutdownHook() método de una ConfigurableApplicationContext en una aplicación de arranque, como tenemos que hacer en la aplicación de núcleo de primavera.

@SpringBootConfiguration
public class ExampleMain {
    @Bean
    MyBean myBean() {
        return new MyBean();
    }

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(ExampleMain.class, args);
        MyBean myBean = context.getBean(MyBean.class);
        myBean.doSomething();

        //no need to call context.registerShutdownHook();
    }

    private static class MyBean {

        @PostConstruct
        public void init() {
            System.out.println("init");
        }

        public void doSomething() {
            System.out.println("in doSomething()");
        }

        @PreDestroy
        public void destroy() {
            System.out.println("destroy");
        }
    }
}

2
2018-04-10 07:00



A todas las respuestas parece faltar el hecho de que es posible que deba completar una parte del trabajo de forma coordinada durante el apagado ordenado (por ejemplo, en una aplicación empresarial).

@PreDestroy le permite ejecutar el código de apagado en los beans individuales. Algo más sofisticado se vería así:

@Component
public class ApplicationShutdown implements ApplicationListener<ContextClosedEvent> {
     @Autowired ... //various components and services

     @Override
     public void onApplicationEvent(ContextClosedEvent event) {
         service1.changeHeartBeatMessage(); // allows loadbalancers & clusters to prepare for the impending shutdown
         service2.deregisterQueueListeners();
         service3.finishProcessingTasksAtHand();
         service2.reportFailedTasks();
         service4.gracefullyShutdownNativeSystemProcessesThatMayHaveBeenLaunched(); 
         service1.eventLogGracefulShutdownComplete();
     }
}

1
2018-06-04 23:58



No expongo ningún extremo ni comienzo (con nohup en segundo plano y sin archivos creados a través de nohup) y detenerse con el script de shell (con KILL PID correctamente y forzar a matar si la aplicación sigue ejecutándose después de 3 minutos) Simplemente creo el archivo jar ejecutable y uso el escritor de archivos PID para escribir el archivo PID y almacenar Jar y Pid en la carpeta con el mismo nombre que el nombre de la aplicación y los guiones del intérprete de órdenes también tienen el mismo nombre con inicio y fin al final. Llamo a estos stop script y start script vía jenkins pipeline también. No hay problemas hasta el momento. Perfectamente funcional para 8 aplicaciones (scripts muy genéricos y fáciles de aplicar para cualquier aplicación).

Clase principal

@SpringBootApplication
public class MyApplication {

    public static final void main(String[] args) {
        SpringApplicationBuilder app = new SpringApplicationBuilder(MyApplication.class);
        app.build().addListeners(new ApplicationPidFileWriter());
        app.run();
    }
}

ARCHIVO YML

spring.pid.fail-on-write-error: true
spring.pid.file: /server-path-with-folder-as-app-name-for-ID/appName/appName.pid

Aquí está el script de inicio (start-appname.sh):

shellScriptFileName=$(basename -- "$0")
shellScriptFileNameWithoutExt="${shellScriptFileName%.*}"
appName=${shellScriptFileNameWithoutExt:6}

PROCESS=$1
PIDS=`ps aux |grep [j]ava.*$appName.*jar | awk {'print $2'}`
if [ -z "$PIDS" ]; then
  echo "No instances of $appName is running..." 1>&2
else
  for PID in $PIDS; do
    echo "Waiting for the process($PID) to finish on it's own for 3 mins..."
    sleep 3m
    echo "FATAL:Killing $appName with PID:$PID."
    kill -9 $PID
  done
fi

# Preparing the java home path for execution
JAVA_EXEC='/usr/bin/java'

# Java Executable - Jar Path Obtained from latest file in directory
JAVA_APP=$(ls -t /server-path-with-folder-as-app-name/$appName/$appName*.jar | head -n1)

# JVM Parameters and Spring boot initialization parameters
JVM_PARAM="-Xms512m -Xmx1024m -Dspring.profiles.active=sit -Dcom.webmethods.jms.clientIDSharing=true"

# To execute the application.
FINAL_EXEC="$JAVA_EXEC $JVM_PARAM -jar $JAVA_APP"

# Making executable command using tilde symbol and running completely detached from terminal
`nohup $FINAL_EXEC  </dev/null >/dev/null 2>&1 &`

echo "$appName has been started successfully."

Aquí está el script stop (stop-appname.sh):

shellScriptFileName=$(basename -- "$0")
shellScriptFileNameWithoutExt="${shellScriptFileName%.*}"
appName=${shellScriptFileNameWithoutExt:5}

# Script to stop the application
PID_PATH="server-path-with-folder-as-app-name-for-PID/$appName/$appName.pid"

if [ ! -f "$PID_PATH" ]; then
   echo "Process Id FilePath($PID_PATH) Not found"
else
pid=`cat $PID_PATH`
    if [ ! -e /proc/$pid -a /proc/$pid/exe ]; then
        echo "$appName was not running.";
    else
       kill $pid;
       echo "Gracefully stopping $appName with PID:$pid..."
    fi
fi

1
2017-07-19 15:07



Si está usando maven puede usar Plugin de ensamblador Maven App.

El daemon mojo (que incrusta JSW) generará un script de shell con argumento de inicio / detención. los stop apagará / matará con gracia su aplicación Spring.

El mismo script se puede usar para usar su aplicación Maven como un servicio de Linux.


0
2018-04-30 06:32