Frage Wie wird eine Spring Boot-Anwendung korrekt heruntergefahren?


Im Spring-Boot-Dokument hieß es: "Jede SpringApplication registriert einen Shutdown-Hook mit der JVM, um sicherzustellen, dass der ApplicationContext beim Beenden ordnungsgemäß geschlossen wird."

Wenn ich klicke ctrl+c Mit dem Shell-Befehl kann die Anwendung ordnungsgemäß heruntergefahren werden. Wenn ich die Anwendung in einer Produktionsmaschine ausführen, muss ich den Befehl verwenden java -jar ProApplicaton.jar. Aber ich kann das Shell-Terminal nicht schließen, sonst schließt es den Prozess.

Wenn ich Befehl wie ausführen nohup java -jar ProApplicaton.jar &Kann ich nicht benutzen ctrl+c um es ordnungsgemäß herunterzufahren.

Was ist der richtige Weg, um eine Spring Boot-Anwendung in der Produktionsumgebung zu starten und zu stoppen?


75
2017-10-24 12:05


Ursprung


Antworten:


Wenn Sie das Aktor-Modul verwenden, können Sie die Anwendung über beenden JMX oder HTTP Wenn der Endpunkt aktiviert ist (Hinzufügen endpoints.shutdown.enabled=true zu deinem application.properties Datei).

/shutdown - Ermöglicht das ordnungsgemäße Herunterfahren der Anwendung (nicht standardmäßig aktiviert).

Abhängig davon, wie ein Endpunkt verfügbar gemacht wird, kann der sensible Parameter als Sicherheitshinweis verwendet werden. Zum Beispiel erfordern empfindliche Endpunkte einen Benutzernamen / ein Passwort, wenn auf sie zugegriffen wird HTTP (oder einfach deaktiviert, wenn die Websicherheit nicht aktiviert ist).

Von dem Spring Boot Dokumentation


45
2017-10-25 14:14



Was @ Jean-Philippe Bonds Antwort betrifft,

Hier ist ein Maven-Schnellbeispiel für den Benutzer maven, um den HTTP-Endpunkt so zu konfigurieren, dass er eine Springboot-Webanwendung mithilfe des Spring-Boot-Starter-Aktors herunterfährt, sodass Sie kopieren und einfügen können:

1.Maus 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

Alle Endpunkte sind aufgelistet Hier:

3.Senden Sie eine Post-Methode zum Herunterfahren der App:

curl -X POST localhost:port/shutdown

Sicherheitshinweis:

Wenn Sie die Shutdown-Methode auth protected benötigen, müssen Sie möglicherweise auch

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

Details konfigurieren:


40
2018-03-29 05:00



Sie können die Springboot-Anwendung dazu bringen, die PID in eine Datei zu schreiben, und Sie können die PID-Datei zum Stoppen oder Neustarten verwenden oder den Status mithilfe eines Bash-Skripts abrufen. Um die PID in eine Datei zu schreiben, registrieren Sie einen Listener wie folgt unter Verwendung von ApplicationPidFileWriter in SpringApplication:

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

Schreiben Sie dann ein Bash-Skript, um die Spring-Boot-Anwendung auszuführen. Referenz.

Jetzt können Sie das Skript zum Starten, Stoppen oder Neustarten verwenden.


24
2018-01-19 11:05



Hier ist eine weitere Option, bei der Sie den Code nicht ändern oder einen heruntergefahrenen Endpunkt nicht anzeigen müssen. Erstellen Sie die folgenden Skripts und verwenden Sie sie zum Starten und Stoppen Ihrer App.

start.sh

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

Startet Ihre App und speichert die Prozess-ID in einer Datei

stop.sh

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

Stoppt Ihre App mithilfe der gespeicherten Prozess-ID

start_silent.sh

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

Wenn Sie die App mit ssh von einem Remote-Computer oder einer CI-Pipeline starten müssen, verwenden Sie stattdessen dieses Skript, um Ihre App zu starten. Wenn Sie start.sh direkt verwenden, kann die Shell hängen bleiben.

Nach z. Re / Bereitstellung Ihrer App können Sie es neu starten mit:

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 stellte mehrere Anwendungslistener bereit, während Sie versuchen, einen Anwendungskontext zu erstellen, von denen einer ApplicationFailedEvent ist. Wir können wissen, ob der Anwendungskontext initialisiert wurde oder nicht.

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

Fügen Sie die obige Listener-Klasse zu SpringApplication hinzu.

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

3
2017-09-01 11:52



Ab Spring Boot 1.5 gibt es keinen standardmäßigen Herunterfahrmechanismus mehr. Einige Spring-Boot-Starter bieten diese Funktionalität:

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

Ich bin der Autor von nr. 1. Der Starter heißt "Hiatus for Spring Boot". Es funktioniert auf der Load-Balancer-Ebene, d. H. Markiert einfach den Dienst als OUT_OF_SERVICE, ohne den Anwendungskontext in irgendeiner Weise zu stören. Dies ermöglicht ein ordnungsgemäßes Herunterfahren und bedeutet, dass der Dienst bei Bedarf für einige Zeit außer Betrieb genommen und dann wieder zum Leben erweckt werden kann. Der Nachteil ist, dass es die JVM nicht stoppt, du wirst es tun müssen kill Befehl. Da ich alles in Containern lief, war das für mich keine große Sache, da ich sowieso anhalten und den Container entfernen muss.

Nr. 2 und 3 basieren mehr oder weniger auf dieser Beitrag von Andy Wilkinson. Sie arbeiten in einer Richtung - sobald sie ausgelöst werden, schließen sie schließlich den Kontext.


2
2017-09-29 13:29



SpringApplication registriert implizit einen Shutdown-Hook mit der JVM, um sicherzustellen, dass ApplicationContext beim Beenden ordnungsgemäß geschlossen wird. Dadurch werden auch alle Bean-Methoden aufgerufen, die mit kommentiert sind @PreDestroy. Das bedeutet, dass wir das nicht explizit verwenden müssen registerShutdownHook() Methode von a ConfigurableApplicationContext in einer Boot-Anwendung, wie wir es in der Federkern-Anwendung tun müssen.

@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



Alle Antworten scheinen die Tatsache zu vermissen, dass Sie während eines ordnungsgemäßen Herunterfahrens (z. B. in einer Unternehmensanwendung) möglicherweise einen Teil der Arbeit koordiniert ausführen müssen.

@PreDestroy ermöglicht es Ihnen, den Abschaltcode in den einzelnen Beans auszuführen. Etwas anspruchsvoller würde so aussehen:

@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



Ich stelle keine Endpunkte aus und starte (mit nohup im hintergrund und ohne out-dateien durch nohup erstellt) und stoppe mit Shell - Skript (mit KILL PID würdevoll und erzwinge das Töten, wenn die App nach 3 Minuten noch läuft). Ich erstelle nur ausführbare jar und benutze PID-Datei-Writer, um PID-Datei schreiben und Jar und Pid in Ordner mit dem gleichen Namen wie der Name der Anwendung und Shell-Skripte haben auch den gleichen Namen mit Start und Stopp am Ende. Ich rufe diese Stopp-Skript und starten Skript über Jenkins-Pipeline auch. Keine Probleme bisher. Perfekt für 8 Anwendungen (sehr generische Skripte und einfach für jede Anwendung zu bewerben).

Hauptklasse

@SpringBootApplication
public class MyApplication {

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

YML-Datei

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

Hier ist das Startskript (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."

Hier ist das Stopp-Skript (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



Wenn Sie Maven verwenden, können Sie das verwenden Maven App Assembler-Plugin.

Der Dämonenmojo (die einbetten JSW) gibt ein Shell-Skript mit Start / Stop-Argument aus. Das stop wird Ihre Spring-Anwendung ordnungsgemäß herunterfahren / beenden.

Das gleiche Skript kann verwendet werden, um Ihre Maven-Anwendung als Linux-Dienst zu verwenden.


0
2018-04-30 06:32