springboot应用如何优雅的关闭

引言

某个项目中采用多中间件、多线程、定时任务等的技术实现,项目启动后,需要优雅的关闭项目,避免项目直接关闭导致某些未运行完成的子进程强制关闭项目数据丢失或部分事务未完成提交。

参考资料

kill 杀死进程

  1. kill -2 pid

    向指定 pid 发送 SIGINT 中断信号,等同于 ctrl+c。也就说,不仅当前进程会收到该信号,而且它的子进程也会收到终止的命令。
  2. kill -9 pid

    向指定 pid 发送 SIGKILL 立即终止信号。程序不能捕获该信号,最粗暴最快速结束程序的方法。
  3. kill -15 pid

    向指定 pid 发送 SIGTERM 终止信号。信号会被当前进程接收到,但它的子进程不会收到,如果当前进程被 kill 掉,它的的子进程的父进程将变成 init 进程 (init 进程是那个 pid 为 1 的进程)。
  4. kill pid

    等同于 kill 15 pid。
  • 在以上命令中,我们不能使用“kill -9”来杀死进程,使用“kill”杀死进程即可。

方案一 (采用)

采用springboot actuator 优雅停机方案

  1. pom.xml 添加 spring-boot-starter-actuator 引用
    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. yml 配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    spring:
    lifecycle:
    # 优雅下线超时时间
    timeout-per-shutdown-phase: 1m

    # 暴露 shutdown 接口 这个端口和服务地址是独立的,非springboot 的启动端口
    # 调用 curl -X POST http://127.0.0.1:18080/actuator/shutdown
    management:
    server:
    ### 端口
    port: 18001
    ### 允许服务地址
    address: 127.0.0.1
    endpoint:
    ### 是否开启下线 默认关闭
    shutdown:
    enabled: true
    endpoints:
    web:
    exposure:
    ### 暴露接口
    include: shutdown

方案二

  1. yml配置graceful
    1
    2
    3
    4
    # 开启优雅停机,默认值:immediate 为立即关闭
    server.shutdown=graceful
    # 设置缓冲期,最大等待时间,默认:30秒
    spring.lifecycle.timeout-per-shutdown-phase=60s
  2. Tomcat 容器关闭实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    public class TomcatGracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
    private volatile Connector connector;

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

    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
    this.connector.pause();
    Executor executor = this.connector.getProtocolHandler().getExecutor();
    if (executor instanceof ThreadPoolExecutor) {
    try {
    log.info("Start to shutdown tomcat thread pool");
    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
    threadPoolExecutor.shutdown();
    if (!threadPoolExecutor.awaitTermination(20, TimeUnit.SECONDS)) {
    log.warn("Tomcat thread pool did not shutdown gracefully within 20 seconds. ");
    }
    } catch (InterruptedException e) {
    log.warn("Fail to shut down tomcat thread pool ", e);
    }
    }
    }
    }

  3. Tomcat 自动装配
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    @Configuration
    @ConditionalOnClass({Servlet.class, Tomcat.class})
    public static class TomcatConfiguration {
    @Bean
    public TomcatGracefulShutdown tomcatGracefulShutdown() {
    return new TomcatGracefulShutdown();
    }

    @Bean
    public EmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory(TomcatGracefulShutdown gracefulShutdown) {
    TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
    tomcatFactory.addConnectorCustomizers(gracefulShutdown);
    return tomcatFactory;
    }
    }

完结