2016-06-10 22 views
1

Ich habe eine Spring-Boot-Anwendung.So verhindern Sie, dass der Spring App-Kontext heruntergefahren wird, bis der Shutdown-Haken ausgelöst wird

Ich habe SmartLifecycle Schnittstelle in meinem Bean implementiert, die Asynchron-snmp-Server startet in es start Methode ist und hält es in es stop Methode ist.

Alles funktioniert gut, außer dass der Hauptanwendungskontext direkt nach dem Start aufhört, so dass meine Server-Bean auch gleich nach dem Start anhält.

Alles, was ich brauche, ist Federkontext zu machen, um nur zu stoppen, wenn der Shutdown-Haken ausgelöst wird.

Dies ist keine Webanwendung, also brauche ich nicht spring-boot-starter-web, die dieses Problem löst, indem Sie den Webserver starten, der den Kontext stoppt, bis der Webserver stoppt.

Ich kann etwas wie CountDownLatch verwenden und darauf warten, dass es in meiner main Methode gleich Null ist, nachdem Kontext beginnt. Somethig wie folgt aus:

public static void main(String[] args) throws InterruptedException { 
    ConfigurableApplicationContext ctx = SpringApplication.run(SnmpTrapRetranslatorApplication.class, args); 
    CountDownLatch snmpServerCloseLatch = ctx.getBean("snmpServerCloseLatch", CountDownLatch.class); 
    snmpServerCloseLatch.await(); 
} 

Und mein Server Bean start Methode wird diese Verriegelung erstellen mit Zählung 1, während stop Methode snmpServerCloseLatch.countDown() nennen.

Diese Technik wird beschrieben here.

Aber was falsch daran ist, dass meine main-Methode ist verantwortlich für warten meine benutzerdefinierte Server-Bean zu stoppen. Ich fühle das einfach nicht richtig.

Wie zum Beispiel spring-boot-starter-web dies tun? Wenn es tomcat startet, wird es weiter ausgeführt, bis der Shutdown-Hook empfangen wird und er keinen Verwaltungscode in der main-Methode haben muss. Es stoppt nur, wenn der Kontext ein Shutdown-Signal empfängt. Das gleiche Verhalten ist zum Beispiel, wenn ich @Scheduled Methode in meiner Bean habe. Spring stoppt den Kontext auch nicht automatisch. Nur auf CTRL-C.

Ich möchte ähnliche Wirkung erzielen. Meine main Methode sollte nur eine Zeile haben: Starten Sie den Kontext. Der Kontext sollte meinen asynchronen Server beim Starten oder Stoppen starten und stoppen (bereits erreicht durch SmartLifecycle) und sollte nicht aufhören, bis das Herunterfahren angefordert wird (CTRL-C, SIGINT usw.).

Antwort

3

Meine Untersuchung führte mich zum Kern des Problems: daemon threads.

Die snmp Server-Implementierung, die ich benutze (snmp4j), verwenden intern Daemon-Threads. Selbst wenn der snmp-Server gestartet wurde, gibt es in JVM keine Live-Benutzer-Threads mehr, weshalb er beendet wird.

TL/DR:

Fügen Sie einfach diese Methode, um jede Bohne (snmp Server-Bean ist guter Kandidat für diese):

@Scheduled(fixedDelay = 1000 * 60 * 60) // every hour 
public void doNothing() { 
    // Forces Spring Scheduling managing thread to start 
} 

(Vergessen Sie nicht, @EnableScheduling auf Ihre Federkonfiguration hinzufügen).

Erläuterung:

zu verhindern Um Frühling Kontext zu stoppen, während SNMP-Server noch läuft, brauchen wir jeden Nicht-Daemon-Thread in JVM am Leben zu sein. Nicht unbedingt main Thread. So können wir main Methode beenden zu beenden.

  1. Wir führen neuen Nicht-Daemon-Thread von unserem Server start Methode des Beans. Dieser Thread wird wait auf einige Sperre in while Schleife für einige running Variable überprüfen, während unsere stop-Methode diese running Variable auf false und notifyAll auf dieses Schloss setzen wird.

    Auf diese Weise ist unser Nicht-Daemon-Thread am Leben, bis der Shotdown-Hook ausgelöst wird (und verhindert, dass JVM beendet wird). Nach Abschaltung Haken, Feder Kontext Lifecycle close Methode ruft alle SmartLifecycleclose Methoden des Bean, die auf SNMP-Server Bean stop Methodenaufruf führen, die zu setzen running auf false führen werden, die zu unserem nicht-Daemon-Thread führt zu stoppen, Dies ermöglicht JVM, ordnungsgemäß zu stoppen.

  2. Oder stattdessen können wir Spring Scheduling-Thread in ähnlicher Weise verwenden. Es ist auch kein Daemon-Thread, sodass JVM nicht beendet wird. Und Spring verwaltet diesen Thread selbst, sodass er automatisch gestoppt wird, wenn der Shutdown-Hook ausgelöst wird.

    Um Springs Scheduling-Thread zu starten, benötigen wir eine beliebige @Scheduled Methode in einer beliebigen Bean.


Ich denke, dass erste (manuell) Ansatz noch mehr „richtig“ ist, während mehr Asynchron-Codierung erfordert (was, wie wir alle wissen, fehleranfällig ist). Wer weiß, wie Spring seine Terminplanung in der Zukunft ändern wird.

1
SpringApplication app = new SpringApplication(Main.class); 
    app.setRegisterShutdownHook(false); 
    ConfigurableApplicationContext applicationContext= app.run(); 
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { 
     @Override 
     public void run() { 
      //do your things 
      applicationContext.close(); 
     } 
    })); 
Verwandte Themen