9
votes

Arrêt progressif de Spring Boot

Je développe une application Spring Boot soutenue par Tomcat intégré et je dois développer un arrêt en douceur avec les étapes suivantes:

  1. arrêter le traitement des nouvelles requêtes HTTP (arrêter le conteneur Web)
  2. traiter toutes les demandes déjà acceptées
  3. arrêter Spring ApplicationContext

* faites les étapes ci-dessus séquentiellement (une par une)

Comment puis-je atteindre cet objectif?

PS Spring Boot 1.5.20.RELEASE, Java 8


8 commentaires

Avez-vous essayé ApplicationListener et context.close ()?


Ce lien pourrait-il vous aider ( dzone.com/articles/graceful-shutdown-spring-boot-applicatio‌ ns )


afaik enregistrez simplement une méthode destroy pour toutes les ressources telles que jdbc, etc. et spring boot fera le reste pour vous lorsque vous supprimez simplement l'application à l'aide de la commande kill


@AnirudhSimha spring n'appelle pas les méthodes destroy sur le processus kill, elles n'appellent qu'après avoir invoqué la méthode close du contexte


J'ai fait la même chose avec l'aide de HA Proxy et Ansible. Mes étapes sont 1) activé la page d'attente donc plus de demande reçue 2) vérifier les journaux de service plus de rotation des 5 dernières minutes. 3) puis arrêt


@AlmasAbdrazak Non, je suppose que ce n'est pas la bonne façon.


@GovindParashar merci, le lien a été utile, mais c'est pour SpringBoot 2.0+, mais en tout cas merci!


@GovindParashar mais vous vous retrouvez avec ApplicationListener;)


4 Réponses :


5
votes

J'ai fini avec:

import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
...
  @Bean
  public GracefulShutdown gracefulShutdown() {
    return new GracefulShutdown();
  }

  @Bean
  public EmbeddedServletContainerFactory servletContainer(final GracefulShutdown gracefulShutdown) {
    TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
    factory.addConnectorCustomizers(gracefulShutdown);
    return factory;
  }
...

quelques haricots supplémentaires:

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {

  private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
  private volatile Connector connector;

  @Override
  public void customize(Connector connector) {
    this.connector = connector;
  }

  @Override
  public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
    log.info("Protocol handler is shutting down");

    this.connector.pause();
    Executor executor = this.connector.getProtocolHandler().getExecutor();
    if (executor instanceof ThreadPoolExecutor) {
      try {
        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
        threadPoolExecutor.shutdown();

        if (!threadPoolExecutor.awaitTermination(30, TimeUnit.SECONDS))
          log.warn("Tomcat thread pool did not shut down gracefully within 30 seconds. Proceeding with forceful shutdown");
        else
          log.info("Protocol handler shut down");

      } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
      }
    }
  }
}


2 commentaires

Blog avec une solution «similaire»: blog.marcosbarbero.com/graceful-shutdown-spring-boot-apps Et voici le lien vers le problème de démarrage de printemps pour un arrêt en douceur du conteneur de servlets: github.com/spring-projects/spring- démarrage / issues / 4657


En regardant cette solution, je crois comprendre qu'elle va forcer une attente de 30 secondes après la demande d'arrêt et seulement après 30 secondes, effectuer un arrêt progressif. Mais le PO veut aussi arrêter les demandes. Comment cela peut-il être réalisé par votre solution?



0
votes

Concernant la réponse de Matthew I. Veuillez noter qu'il utilise JDK 'ThreadPoolExecutor' alors qu'il devrait utiliser Tomcat ThreadPoolExecutor. ==> 'org.apache.tomcat.util.threads.ThreadPoolExecutor;

Sinon, le hook d'arrêt n'attendra pas vraiment l'arrêt des threads Tomcat.


1 commentaires

Bonjour Johnny, vous devriez créer un commentaire sur la réponse au lieu d'en créer un nouveau :)



0
votes

Son démarrage Simple, Spring lui-même fournit des fonctionnalités. https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-application-exit

void shoutdown(){
       System.out.println("====+= Shoutdown +++===");
       System.exit(SpringApplication.exit(apc, this.exitCodeGenerator()));

   }

Vous pouvez voir en sortie, tous les threads actuels étant fermés. Production:

==== + = Shoutdown +++ ===

  • 2020-06-09 11: 21: 45 543 DEBUG [main] [cujcEnableEncryptablePropertiesBeanFactoryPostProcessor] Événement d'application déclenché: ExitCodeEvent 09/06/2020 11: 21: 45 543 DEBUG [main] [cujcEnableEncryptablePropertiesBeanFactoryPostProcessor] Événement d'application déclenché le 09/06/2020 : 21: 45 546 DEBUG [main] [cujcEnableEncryptablePropertiesBeanFactoryPostProcessor] Événement d'application déclenché: ContextClosedEvent 09/06/2020 11: 21: 45 546 DEBUG [main] [cujcEnableEncryptablePropertiesBeanFactoryPostProcessor] Événement d'application déclenché: 21-06-09 INFOClosedEvent main] [oakafka.clients.producer.KafkaProducer] [Producteur clientId = producteur-1] Fermeture du producteur Kafka avec timeoutMillis = 30000 ms. 2020-06-09 11: 21: 45,548 DEBUG [kafka-producteur-network-thread | producteur-1] [oakclients.producer.internals.Sender] [Producteur clientId = producteur-1] Début de l'arrêt du thread d'E / S du producteur Kafka, envoi des enregistrements restants. 2020-06-09 11: 21: 45,551 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom connections-closed: 2020-06-09 11: 21: 45,554 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom connections-created: 2020-06-09 11: 21: 45,554 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom Success-authentication: 2020-06-09 11: 21: 45,558 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec nom échoué-authentification: 09/06/2020 11: 21: 45,558 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom octets-envoyés-reçus: 09/06/2020 11: 21: 45,559 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom octets envoyés: 09/06/2020 11: 21: 45,559 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec nom octets reçus: 09/06/2020 11: 21: 45,560 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom select-time: 09/06/2020 11: 21: 45,561 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom io-time: 09/06/2020 11: 21: 45,570 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec nœud de nom - 1. octets-envoyé 09/06/2020 11: 21: 45,570 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec nœud de nom - 1. octets-reçus 09/06/2020 11: 21: 45,570 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec nom node - 1.latency 09/06/2020 11: 21: 45,571 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom node-0.bytes-sent 09/06/2020 11: 21: 45,571 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom node-0.bytes-recu 09/06/2020 11: 21: 45,573 DEBUG [kafka-producteur-network-thread | producteur-1] [org.apache.kafka.common.metrics.Metrics] Capteur supprimé avec le nom node-0.latency 09/06/2020 11: 21: 45,573 DEBUG [kafka-producteur-network-thread | producteur-1] [oakclients.producer.internals.Sender] [Producteur clientId = producteur-1] L'arrêt du thread d'E / S du producteur Kafka est terminé. 2020-06-09 11: 21: 45,607 DEBUG [main] [oakafka.clients.producer.KafkaProducer] [Producteur clientId = producteur-1] Le producteur de Kafka a été fermé 2020-06-09 11: 21: 45,611 DEBUG [main] [o.hibernate.internal.SessionFactoryImpl] HHH000031: Clôture 09/06/2020 11: 21: 45 611 DÉBOGAGE [main] [ohtype.spi.TypeConfiguration $ Scope] Dé-portée TypeConfiguration [org.hibernate.type.spi.TypeConfiguration $ Scope @ 5dfd31f4] from SessionFactory [org.hibernate.internal.SessionFactoryImpl@62a54948] 09/06/2020 11: 21: 45,612 DEBUG [main] [ohsiAbstractServiceRegistryImpl] Détruire implicitement ServiceRegistry lors du désenregistrement de tous les services enfants ServiceRegistries 09/06/2020 11: 21: 45,613 DEBUG [main] [ohbriBootstrapServiceRegistryImpl] Détruire implicitement le registre Boot-strap lors de la désinscription de tous les ServiceRegistries enfants 2020-06-09 11: 21: 45,613 INFO [main] [com.zaxxer.hikari.HikariDataSource] HikariPool -1 - Arrêt lancé ... 2020-06-09 11: 21: 45,754 INFO [main] [com.zaxxer.hikari.HikariDataSource] HikariPool-1 - Arrêt terminé .


0 commentaires

0
votes

La prise en charge de l'arrêt progressif a été ajoutée dans Spring Boot 2.3 (sortie en mai 2020). Cela permet aux requêtes actives de se terminer avant de fermer le contexte et d'arrêter le conteneur.

À partir des notes de version :

L'arrêt progressif est pris en charge avec les quatre serveurs Web intégrés (Jetty, Reactor Netty, Tomcat et Undertow) et avec les applications Web réactives et basées sur des servlets. Lorsqu'il est activé à l'aide de server.shutdown=graceful , à l'arrêt, le serveur Web n'autorisera plus les nouvelles demandes et attendra une période de grâce pour que les demandes actives se terminent. La période de grâce peut être configurée à l'aide de spring.lifecycle.timeout-per-shutdown-phase .

Ainsi, lorsque l'arrêt progressif est activé, l'application exécutera les étapes suivantes de manière séquentielle lors de l'arrêt:

  • arrêter d'accepter de nouvelles demandes
  • attendra un certain temps pour traiter les demandes déjà acceptées
  • arrêter le conteneur
  • arrêter le serveur intégré

  • Pour activer l'arrêt server.shutdown=graceful , ajoutez server.shutdown=graceful aux propriétés (par défaut, il est défini sur immediate ).
  • La période de grâce peut être configurée à l'aide de la spring.lifecycle.timeout-per-shutdown-phase (exemple: spring.lifecycle.timeout-per-shutdown-phase=1m .

Pour Spring Boot <2.3, vous devrez bricoler le connecteur du serveur pour arrêter d'accepter de nouvelles demandes, comme expliqué dans ce problème Spring GitHub .


0 commentaires