SpringBoot优雅停机

1,248 阅读3分钟

简介

项目中难免会遇到需要关闭应用的情况,如更新项目等等。一般我们选择用户量较少的时间段停止项目以降低风险。但是如果在我们停止项目的过程中任有用户在操作或者是有定时任务在执行,那这些操作会被终止,并且有很大概率造成数据异常。这不是我们想看到的。

在最新的 spring boot 2.3 版本中,内置了graceful shutdown功能,不需要再自行扩展容器线程池来处理,目前 spring boot 嵌入式支持的 web 服务器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及反应式和基于 Servlet 的 web 应用程序都支持优雅停机功能。在此之前的版本咱不研究。本文使用的版本是2.3.0.RELEASE

实战

要想使用优雅停机功能,需要引入actuator的jar包

pom

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

yml

management:
  endpoint:
    shutdown:
      # 启用shutdown
      enabled: true
  endpoints:
    web:
      exposure:
        include: "*"

server:
  # 开启优雅停机,默认IMMEDIATE是立即关机
  shutdown: graceful
spring:
  lifecycle:
    # 缓冲时间,默认时间为30S,意味着最多等待30S,超时无论线程任务是否执行完毕都会停机处理,一定要合理合理设置。
    timeout-per-shutdown-phase: 20s
@RestController
@Slf4j
public class TestController {
    @GetMapping("/sleep")
    public String sleep(Integer timeout) throws InterruptedException {
        log.info("begin sleep:{}", timeout);
        for (Integer i = 0; i < timeout; i++) {
            // 模拟业务耗时处理流程
            TimeUnit.SECONDS.sleep(1);
            log.info("i=======>{}", i);
        }
        log.info("end sleep:{}", timeout);
        return "sleep:" + timeout;
    }
}

项目启动后使用actuator提供的/actuator/shutdown请求(post)

使用 postman 模拟一个POST请求,请求url默认为http://ip:port/actuator/shutdown

或者在linux上直接执行(建议使用 shell 脚本部署项目,使用下面的 curl 来代替原有的 kill -9 pid)

curl -X POST host:port/shutdown

返回

{"message":"Shutting down, bye..."}

系统接收到关闭信号后的请求将不再执行

或者直接点击 idea 的关闭按钮(注意只能点击一次

这三种操作都是等价的,开发时三选一即可。应用部署上线建议使用第二种。第一种由于安全性问题会做一些配置,下文会讲到。

分析

如果是请求参数timeout<20会正常执行完任务,如果>20 则会强制停止

小于20 时

大于20 时

最后会抛出异常

最终说明优雅关闭成功

优化

如果只是这样配置的话,还是存在很大的安全问题,当外人知道项目ip地址和端口号时,谁都可以模拟该请求关闭应用

按如下配置修改配置文件更加安全

management:
  endpoint:
    shutdown:
      # 启用shutdown
      enabled: true
  endpoints:
    web:
      exposure:
        include: "*"
      # 自定义管理端点的前缀(保证安全)
      base-path: /MyActuator
  server:
    # 自定义端口
    port: 9999
    # 不允许远程管理连接(不允许外部调用保证安全)
    address: 127.0.0.1

此时的请求地址变为:http://127.0.0.1:9999/MyActuator/shutdown