Spring Boot正常关机

时间:2019-05-08 12:13:11

标签: java spring spring-boot tomcat

我正在开发一个由嵌入式Tomcat支持的Spring Boot应用程序,我需要通过以下步骤来正常关闭:

  1. 停止处理新的HTTP请求(停止Web容器)
  2. 处理所有已接受的请求
  3. 关闭Spring ApplicationContext

*依次(依次)执行上述步骤

我该如何实现?

P.S。 Spring Boot 1.5.20.RELEASE,Java 8

4 个答案:

答案 0 :(得分:2)

我最终得到了:

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

更多其他豆:

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

答案 1 :(得分:0)

关于马修一世。 请注意,他应该使用JDK'ThreadPoolExecutor',而他应该使用Tomcat ThreadPoolExecutor。 ==>'org.apache.tomcat.util.threads.ThreadPoolExecutor;

否则,关闭挂钩将不会真正等待Tomcat线程停止。

答案 2 :(得分:0)

它很简单,Spring boot本身提供了功能。 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()));

   }

您可以在输出中看到所有当前线程都已关闭。 输出:

==== + =关机+++ ===

  • 2020-06-09 11:21:45,543 DEBUG [main] [c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor] 引发的应用程序事件:ExitCodeEvent 2020-06-09 11:21:45,543 DEBUG [main] [c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor] 引发的应用程序事件:ExitCodeEvent 2020-06-09 11:21:45,546 DEBUG [main] [c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor] 引发应用程序事件:ContextClosedEvent 2020-06-09 11:21:45,546 DEBUG [main] [c.u.j.c.EnableEncryptablePropertiesBeanFactoryPostProcessor] 引发应用程序事件:ContextClosedEvent 2020-06-09 11:21:45,547 INFO [主要] [o.a.kafka.clients.producer.KafkaProducer] [生产者 clientId = producer-1]使用timeoutMillis =关闭Kafka生产者 30000毫秒2020-06-09 11:21:45,548 DEBUG [kafka-生产者网络线程| producer-1] [o.a.k.clients.producer.internals.Sender] [生产者 clientId = producer-1]开始关闭Kafka生产者I / O线程, 发送剩余的记录。 2020-06-09 11:21:45,551 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称连接已关闭:2020-06-09 11:21:45,554 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称创建的连接名称:2020-06-09 11:21:45,554 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称成功身份验证:2020-06-09 11:21:45,558 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称失败的身份验证:2020-06-09 11:21:45,558 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 发送的名称字节为:2020-06-09 11:21:45,559 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称字节已发送:2020-06-09 11:21:45,559 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称字节已接收:2020-06-09 11:21:45,560 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称选择时间:2020-06-09 11:21:45,561 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称io-time:2020-06-09 11:21:45,570 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node--1.bytes-sent 2020-06-09 11:21:45,570 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node--1.bytes-received 2020-06-09 11:21:45,570 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node--1.latency 2020-06-09 11:21:45,571 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node-0.bytes-sent 2020-06-09 11:21:45,571 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node-0.bytes-received 2020-06-09 11:21:45,573 DEBUG [kafka-生产者网络线程| producer-1] [org.apache.kafka.common.metrics.Metrics]删除了传感器 名称为node-0.latency 2020-06-09 11:21:45,573 DEBUG [kafka-生产者网络线程| producer-1] [o.a.k.clients.producer.internals.Sender] [生产者 clientId = producer-1]关闭Kafka生产者I / O线程 完成。 2020-06-09 11:21:45,607 DEBUG [main] [o.a.kafka.clients.producer.KafkaProducer] [Producer clientId = producer-1] Kafka生产者已关闭2020-06-09 11:21:45,611调试[主要] [o.hibernate.internal.SessionFactoryImpl] HHH000031:闭幕2020-06-09 11:21:45,611 DEBUG [main] [o.h.type.spi.TypeConfiguration $ Scope]取消作用域 类型配置 来自的[org.hibernate.type.spi.TypeConfiguration$Scope@5dfd31f4] SessionFactory [org.hibernate.internal.SessionFactoryImpl@62a54948] 2020-06-09 11:21:45,612 DEBUG [main] [o.h.s.i.AbstractServiceRegistryImpl]隐式 取消所有儿童的注册后销毁ServiceRegistry 服务注册表2020-06-09 11:21:45,613 DEBUG [main] [o.h.b.r.i.BootstrapServiceRegistryImpl]隐式 在取消所有孩子的注册时销毁Bootstrap注册表 服务注册表2020-06-09 11:21:45,613信息 [main] [com.zaxxer.hikari.HikariDataSource] HikariPool-1-关闭 启动... 2020-06-09 11:21:45,754信息 [main] [com.zaxxer.hikari.HikariDataSource] HikariPool-1-关闭 完成。

答案 3 :(得分:0)

Spring Boot 2.3(于2020年5月发布)中添加了正常关机支持。这样可以在关闭上下文并关闭容器之前完成活动请求。

来自release notes

所有四个嵌入式Web服务器(Jetty,Reactor Netty,Tomcat和Undertow)以及响应式和基于Servlet的Web应用程序都支持正常关机。使用server.shutdown=graceful启用后,关闭时,Web服务器将不再允许新请求,并将等待宽限期以完成活动请求。宽限期可以使用spring.lifecycle.timeout-per-shutdown-phase进行配置。

因此,启用正常关机后,应用程序将在关机时依次执行以下步骤:

  • 停止接受新请求
  • 将等待一段时间来处理已接受的请求
  • 停止容器
  • 停止嵌入式服务器

  • 要启用正常关机,请将server.shutdown=graceful添加到属性中(默认情况下,它设置为immediate)。
  • 宽限期可以使用spring.lifecycle.timeout-per-shutdown-phase属性进行配置(例如:spring.lifecycle.timeout-per-shutdown-phase=1m

对于Spring Boot <2.3,您将修改服务器的连接器以停止接受新请求,如this Spring GitHub issue中所述。