当前位置:   article > 正文

springboot优雅停机实现方案_springboot 优雅停止正在执行的任务

springboot 优雅停止正在执行的任务

springboot优雅停机实现方案

背景

spring boot默认是不会优雅关闭的,在没有启用任何优雅关闭机制的情况下,Spring Boot 应用程序将在收到信号后立即终止,此时一些没有执行完的程序就会直接退出,可能导致业务逻辑执行失败,在一些业务场景下会出现数据不一致的情况。

不使用优雅停机:1.在一体化系统中录入拨款申请等场景,直接结束进程,会导致用户录入以及后续的记账等流程直接中断。2.在使用定时任务执行一些规则时,插入表数据未用事务时,会导致一批执行的规则数据不一致,还要筛选哪些规则执行哪些规则没执行。3.分布式业务存在多线程的情况下,执行关联的业务逻辑处理,有些业务逻辑可能没执行,也会造成数据不一致的情况。4.在服务调用外部接口时,会直接调用失败。

使用优雅停机:1.用户可以把当前的操作请求执行完才会停止服务。2.对于定时任务可以避免规则部分执行的情况。3.对于多线程执行业务逻辑场景,可以避免数据不一致的情况。4.服务在调用外部接口时,会在等待的时间内调用外部接口成功。

注意:以上举例的场景,仅限于执行业务的时间在优雅停机设置的容忍时间(设置的强制结束时间)内,即优雅停机可以很大几率解决一些问题。

当前解决方式

  • spring boot 2.3以下版本,引入spring-boot-starter-actuator监控类库,它其中一个功能支持优雅关闭。

  • spring boot 2.3版本开始,自己集成了优雅关闭,无需再引入上方类库即可实现优雅关闭。

  • 注意:spring-boot-starter-actuator文档中说是支持优雅关闭,但仅仅是spring层面上的,不和tomcat等容器挂钩,直到spring boot 2.3开启自带的优雅关闭后才真正能实现,也就是说2.3之前的版本根本实现不了优雅关闭,需要自己来进一步按照使用的容器做处理才行。

实现优雅停机具体步骤

springboot2.3 以上版本的处理方法

在application.yml中增加:

# 开启优雅关闭
server:
  shutdown: graceful
# 配置强制结束时间,不配置的话默认30s
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 配置好后就支持优雅关闭了,linux端只需要在脚本文件中设置关闭命令 kill PID,即根据PID结束相关服务的进程。
springboot1.X 版本的处理方法(此处使用的容器为tongweb)
  • 引入spring-boot-starter-actuator的maven类库
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
  • 1
  • 2
  • 3
  • 4
  • 创建相关类:容器使用东方通tongWeb(改成你想用的容器即可,例如tomcat)
import com.tongweb.container.connector.Connector;
import com.tongweb.springboot.v1.x.embed.TongWebConnectorInitializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 优雅关闭
 * @Author:yangshen
 * Date:2023年10月16日
 */
@Slf4j
public class GracefulShutdown implements TongWebConnectorInitializer,
        ApplicationListener<ContextClosedEvent> {
  private volatile Connector connector;

  /**
   * 30s强制关闭 当超过30秒时,会强制结束进程
   */
  private static final int TIMEOUT = 30;


  /**
   * 关闭时触发
   *
   * @param event
   */
  @Override
  public void onApplicationEvent(ContextClosedEvent event) {
    this.connector.pause();
    Executor executor = this.connector.getProtocolHandler().getExecutor();
    if (executor instanceof ThreadPoolExecutor) {
      try {
        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
        threadPoolExecutor.shutdown();
        if (!threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.SECONDS)) {
          log.warn("TongWeb thread pool did not shut down gracefully within "
                  + TIMEOUT + " seconds. Proceeding with forceful shutdown");
        }
      } catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
      }
    }
  }

  /**
   * 自定义链接
   */
  @Override
  public void init(Connector connector) {
    this.connector = connector;
  }
}
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
import com.boss.payreal.controller.GracefulShutdown;
import com.tongweb.springboot.v1.x.embed.TongWebEmbedServletContainerFactory;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


/**
 * 优雅停机配置:
 * @Author: yangshen
 * Date: 2023年10月16日
 */
@Configuration
public class ShutdownConfig {

  /**
   * 优雅关闭bean,用于接受shutdown事件
   */
  @Bean
  public GracefulShutdown gracefulShutdown() {
    return new GracefulShutdown();
  }

  /**
   * tongWeb配置优雅关闭
   */
  @Bean
  public EmbeddedServletContainerCustomizer tongWebCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
      @Override
      public void customize(ConfigurableEmbeddedServletContainer container) {
        if (container instanceof TongWebEmbedServletContainerFactory) {
          ((TongWebEmbedServletContainerFactory) container)
                  .addConnectorCustomizers(gracefulShutdown());
        }

      }
    };
  }
}
  • 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
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

优雅停机停止方式

无论是springboot1.x版本还是2.3以上版本,皆有两种触发优雅停机的方式:

1.通过 curl -X POST http://host:port/actuator/shutdown

2.通过使用kill pid 命令,不能使用kill -9

两种方式的区别:

方式1 不安全,需要在配置文件中额外配置shutdown端点开启,配置额外的自定义端口,以及自定义路径来保证安全性,而且这种方式只是暂停容器接受请求,并不会结束进程,还需要在脚本里加kill-9 pid 才会结束进程实现优雅停机,比较繁琐。

方式2 比较安全,脚本里直接kill pid即可,会实现优雅停机并且结束进程,springboot1.x版本不用配置yml文件中的监控相关配置,直接用配置类即可

方式1需要增加如下配置:

#监控相关配置
management:
  endpoint:
    # 开启
    shutdown:
      enabled: true
  endpoints:
    web:
      # 只允许shutdown,为了安全,其它想要监控自行配置
      exposure:
        include: "shutdown"
      # 自定义请求路径,为了安全
      base-path: /xxx
  server:
    #自定义请求端口,为了安全
    port: 7080
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

最终方案

因为目前我们的系统springboot版本为1.5.12,故采用springboot1.X的优雅停机方案,只需要引入spring-boot-starter-actuator的maven类库,在配置类中实现代码即可,无需配置yml配置文件中的关于actuator的配置,调用优雅停机方式采用方式2,kill pid触发优雅停机。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/767560
推荐阅读
相关标签
  

闽ICP备14008679号