Fahren Sie die Spring Boot-Anwendung programmgesteuert herunter

109

Wie kann ich eine Spring Boot- Anwendung programmgesteuert herunterfahren, ohne die VM zu beenden ?

In anderen Werken, was ist das Gegenteil von

new SpringApplication(Main.class).run(args);
Axel Fontaine
quelle
1
Guter Punkt! Das Aufrufen von close () sollte den Job erledigen.
Axel Fontaine
Mögliches Duplikat von Wie wird eine Spring Boot-Anwendung korrekt heruntergefahren?
Anand Varkey Philips
@ AnandVarkeyPhilips Nein, das ist es definitiv nicht. In diesem Fall geht es um eine API, in dem anderen um eine Möglichkeit für Ops, dies zu tun.
Axel Fontaine
Okay .. Dieser Fragenlink könnte anderen helfen. Soll ich den obigen Kommentar löschen?
Anand Varkey Philips

Antworten:

111

Das Schließen von a SpringApplicationbedeutet im Grunde das Schließen des Basiswerts ApplicationContext. Die SpringApplication#run(String...)Methode gibt Ihnen das ApplicationContextals ConfigurableApplicationContext. Sie können es dann close()selbst tun.

Beispielsweise,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to shut down...
        ctx.close();
    }
}

Alternativ können Sie die static SpringApplication.exit(ApplicationContext, ExitCodeGenerator...)Hilfsmethode verwenden, um dies für Sie zu tun. Beispielsweise,

@SpringBootApplication
public class Example {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(Example.class, args);
        // ...determine it's time to stop...
        int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
            @Override
            public int getExitCode() {
                // no errors
                return 0;
            }
        });

        // or shortened to
        // int exitCode = SpringApplication.exit(ctx, () -> 0);

        System.exit(exitCode);
    }
}
Sotirios Delimanolis
quelle
1
Wenn ich ctx.close () benutze; Es ist nicht nötig, System.exit (n) am Ende aufzurufen, oder? Kontext close () sollte System.exit () enthalten?
Denys
2
@Denys Nein, der Kontext beendet den Java-Prozess beim Schließen nicht. Der Exit in meinem Beispiel zeigt nur, wie das ExitCodeGeneratorverwendet werden kann. Sie können einfach von der mainMethode zurückkehren, um ordnungsgemäß zu beenden (Beendigungscode 0).
Sotirios Delimanolis
78

In einer Spring Boot-Anwendung können Sie so etwas verwenden

ShutdownManager.java

import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;

@Component
class ShutdownManager{

    @Autowired
    private ApplicationContext appContext;

    public void initiateShutdown(int returnCode){
        SpringApplication.exit(appContext, () -> returnCode);
    }
}
Snovelli
quelle
16
Upvote für den Nachweis, dass das ApplicationContextautomatisch in andere Bohnen injiziert werden kann.
Anthony Chuinard
1
@snovelli Wie rufe ich die Methode zum Herunterfahren auf? initiateShutdown (x), x = 0?
StackOverFlow
Bei bedingtem Herunterfahren kann dies ausgeführt werden. SpringApplication.run (..). Close () wird ausgeführt, wenn das Programm abgeschlossen ist.
Abubacker Siddik
warum SpringApplication. (appContext, () -> returnCode); warum kann appContext.close () nicht. Was ist der Unterschied ?
Rams
@StackOverFlow Sie müssen die Bean dort injizieren, wo Sie sie benötigen, und dann den Rückkehrcode wie vorgeschlagen übergeben (x = 0), wenn sie ordnungsgemäß heruntergefahren wird. Zum Beispiel könnten Sie die Shutdown Manager in eine RestController injizieren und Remote Shutdown lassen, oder man könnte es in ein Health injizieren Überwachung , dass die JVM nach unten bei fehlenden nachgelagerten Dienstleistungen geschlossen würde
snovelli
36

Dies funktioniert, auch wenn es gedruckt wird.

  SpringApplication.run(MyApplication.class, args).close();
  System.out.println("done");

Also .close()nachher hinzufügenrun()

Erläuterung:

public ConfigurableApplicationContext run(String... args)

Führen Sie die Spring-Anwendung aus und erstellen und aktualisieren Sie einen neuen ApplicationContext. Parameter:

args - die Anwendungsargumente (normalerweise von einer Java-Hauptmethode übergeben)

Rückgabe: ein laufender ApplicationContext

und:

void close()Schließen Sie diesen Anwendungskontext und geben Sie alle Ressourcen und Sperren frei, die die Implementierung möglicherweise enthält. Dies beinhaltet das Zerstören aller zwischengespeicherten Singleton-Beans. Hinweis: Ruft in einem übergeordneten Kontext nicht close auf. Elternkontexte haben ihren eigenen, unabhängigen Lebenszyklus.

Diese Methode kann ohne Nebenwirkungen mehrmals aufgerufen werden: Nachfolgende Abschlussaufrufe in einem bereits geschlossenen Kontext werden ignoriert.

Im Grunde genommen wird der übergeordnete Kontext nicht geschlossen. Deshalb wird die VM nicht beendet.

ACV
quelle
2
Nur zur Erinnerung, diese Lösung funktioniert für kurzlebige Prozesse wie Batch, verwendet sie jedoch nicht für Spring MVC-Anwendungen. Die Anwendung wird gerade nach dem Booten heruntergefahren.
Michael COLL
@MichaelCOLL Die Frage ist, wie eine Spring Boot App unabhängig vom Typ programmgesteuert heruntergefahren werden kann. Es funktioniert auch für Spring MVC
ACV
1
@ACV Du hast Recht, es funktioniert, es funktioniert sehr gut. Aber für eine App, die aktiv bleiben muss (wie die Spring MVC-App), ist dies meiner Meinung nach nicht der gute Weg, dies zu tun. In meinem Fall habe ich verwendet SpringApplication.exit(appContext, () -> returnCode).
Michael COLL
1
Auf welche VM beziehen Sie sich in Ihrer letzten Zeile? Wenn Sie Ihre Spring Boot-Anwendung mit starten SpringApplication.run(MyApplication.class, args), gibt es keinen übergeordneten Kontext. Es gibt nur einen Kontext, den Kontext run, der von Ihnen erstellt und zurückgegeben wird , den Sie dann sofort close. @ Michael ist richtig. Dies funktioniert nicht für Programme, die nach der Initialisierung des Spring-Kontexts etwas tun müssen. Dies sind die meisten Programme.
Erlöser
@Savior JVM. Es gibt einen übergeordneten Kontext. Hier geht es darum, wie eine Spring-Boot-Anwendung heruntergefahren wird. Normalerweise fahren Sie Webanwendungen nicht auf diese Weise herunter. Daher wird dieser Mechanismus normalerweise für kurzlebige Anwendungen verwendet, die etwas tun und dann aufhören müssen. Standardmäßig läuft Spring Boot auch nach Abschluss der Stapelverarbeitung weiter, sodass Sie diesen Mechanismus dort verwenden möchten.
ACV
3

In der Anwendung können Sie verwenden SpringApplication. Dies hat eine statische exit()Methode, die zwei Argumente akzeptiert: das ApplicationContextund ein ExitCodeGenerator:

dh Sie können diese Methode deklarieren:

@Autowired
public void shutDown(ExecutorServiceExitCodeGenerator exitCodeGenerator) {
    SpringApplication.exit(applicationContext, exitCodeGenerator);
}

Innerhalb der Integrationstests können Sie dies erreichen, indem Sie @DirtiesContextAnmerkungen auf Klassenebene hinzufügen :

  • @DirtiesContext(classMode=ClassMode.AFTER_CLASS) - Der zugehörige ApplicationContext wird nach der Testklasse als fehlerhaft markiert.
  • @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) - Der zugehörige ApplicationContext wird nach jeder Testmethode in der Klasse als fehlerhaft markiert.

dh

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = {Application.class},
    webEnvironment= SpringBootTest.WebEnvironment.DEFINED_PORT, properties = {"server.port:0"})
@DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_CLASS)
public class ApplicationIT {
...
Zauberkünstler
quelle
OK. Wo soll ich ExecutorServiceExitCodeGenerator bekommen? Wenn es sich um eine Bean handelt, können Sie den Erstellungs-Snippet-Code anzeigen (und aus welcher Klasse er erstellt wird)? In welche Klasse soll die Methode shutDown (ExecutorServiceExitCodeGenerator exitCodeGenerator) gestellt werden?
Vlad G.
2

Dadurch wird sichergestellt, dass die SpringBoot-Anwendung ordnungsgemäß geschlossen und die Ressourcen wieder für das Betriebssystem freigegeben werden.

@Autowired
private ApplicationContext context;

@GetMapping("/shutdown-app")
public void shutdownApp() {

    int exitCode = SpringApplication.exit(context, (ExitCodeGenerator) () -> 0);
    System.exit(exitCode);
}
Samim Aftab Ahmed
quelle