1 Star 0 Fork 28

柚子 / notebook

forked from JustryDeng / notebook 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
[18]spring-boot优雅停机.md 9.97 KB
一键复制 编辑 原始数据 按行查看 历史
JustryDeng 提交于 2024-01-16 10:48 . spring-boot优雅停机

spring-boot优雅停机

相关知识说明

1. 什么是Web 容器优雅停机行为

Web 容器优雅停机行为指的是在关闭容器时,让当前正在处理的请求处理完成或者等待一段时间,让正在处理的请求完成后再关闭容器,而不是直接强制终止正在处理的请求。这样可以避免正在处理的请求被中断,从而提高系统的可用性和稳定性

一般来说,Web 容器的优雅停机行为需要满足以下几个条件:

  1. 等待正在处理的请求完成,不再接受新的请求
  2. 如果等待时间超过了一定阈值,容器可以强制关闭
  3. 在容器关闭之前,需要给客户端一个响应,告知他们当前正在关闭容器,不再接受新的请求

2. 优雅停机的目的

如果没有优雅停机,服务器此时直接直接关闭(kill -9),那么就会导致当前正在容器内运行的业务直接失败,在某些特殊的场景下产生脏数据

3. 优雅停机具体行为

在服务器执行关闭(kill -2)时,会预留一点时间使容器内部业务线程执行完毕;增加了优雅停机配置后, 此时容器也不允许新的请求进入。

目前版本(2.3.4.RELEASE)的Spring Boot 优雅停机支持Jetty, Reactor Netty, TomcatUndertow 以及反应式和基于 Servlet 的 web 应用程序都支持优雅停机功能。

新请求的处理方式跟web服务器有关,Reactor Netty、 Tomcat将停止接入请求,Undertow的处理方式是返回503。

具体行为,如下表所示:

web 容器名称 行为说明
tomcat 9.0.33+ 停止接收请求,客户端新请求等待超时
Reactor Netty 停止接收请求,客户端新请求等待超时
Undertow 停止接收请求,客户端新请求直接返回 503

不同的 Web 容器实现优雅停机的方式可能会有所不同,但是一般都会提供相关的配置选项或者 API 接口来实现这个功能。

另外,和SpringBoot内嵌的WEB服务器类似,其他的非SpringBoot内嵌WEB服务器,也可以进行设置。

下面是 Nginx 和 Apache 的优雅停机配置:

  • Nginx 可以通过配置文件中的 worker_shutdown_timeout 选项来设置等待时间
  • Apache 可以通过 graceful-stop 命令来实现优雅停机。

spring-boot内嵌tomcat优雅停机(示例)

第一步:在配置文件中启用优雅停机支持

server:
  # 设置为优雅停机。默认为immediate,立即停机
  shutdown: graceful

# 设置缓冲参数timeout-per-shutdown-phase
# 在timeout-per-shutdown-phase时间内的某个时间点如果线程执行完毕了,则会马上完成停机
# 在timeout-per-shutdown-phase时间内如果线程无法执行完毕,则一旦过了timeout-per-shutdown-phase时间,则会被强制停机
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

第二步:进行优雅停机

正常部署启动程序,然后使用以下方式进行停止程序,即可触发优雅停机

  • 方式一:kill {进程id}

    kill {进程id},等价于kill -15 {进程id},表示终止进程;不能用kill -9 {进程id},kill -9表示杀死进程,会立即杀死进程

  • 方式二:调用/actuator/shutdown端点

    提示:前提是,暴露了此端点,才可以调用

第三步:测试一下

  • 测试步骤1:准备一个spring-boot项目(本人用的是spring-boot 2.6.4版本),然后按照第一步配置启用优雅停机支持

  • 测试步骤2:编写一个简单的测试代码

    import org.apache.commons.lang3.RandomStringUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TestController {
        
        @RequestMapping("/test")
        public String test() throws InterruptedException {
            Thread.sleep(10000);
            return RandomStringUtils.random(10 , true, true);
        }
    }
  • 测试步骤3:打包项目,部署到linux后启动项目

    在IDE(如:idea)中直接启动也可

  • 测试步骤4:依次进行以下步骤,然后观察结果及程序日志

    1. 新开一个shell窗口,发送请求
    2. 新开一个shell窗口,执行kill {进程id}指令
    3. 新开一个shell窗口,再次发送请求

    1的请求最后是成功的:

    因为这时还没触发优雅停机

    image-20230611124227532

    2的操作截图:

    触发优雅停机,此时1还在执行中,3还没执行

    image-20230611124312921

    3的请求是失败的:

    因为tomcat已经收到优雅停机指令了,所以拒绝了3的请求,但是接受优雅停机前收到1的请求还是会执行完毕才停机的

    image-20230611124335481

    最后我们观察程序日志

      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.6.4)
    
    2023-06-11 12:37:29.660  INFO 31419 --- [           main] com.ideaaedi.demo.StartUp                : Starting StartUp v1.0.0 using Java 11.0.1 on iZm5ea6hfv6pmn30ook2l7Z with PID 31419 (/temp/demo-1.0.0.jar started by root in /temp)
    2023-06-11 12:37:29.671  INFO 31419 --- [           main] com.ideaaedi.demo.StartUp                : No active profile set, falling back to 1 default profile: "default"
    2023-06-11 12:37:33.036  INFO 31419 --- [           main] c.i.c.register.FeatureRegistrar          : registry bean 'parameterRecorderAdvice' with ConstructorArg includePrefixes -> [com.ideaaedi], excludePrefixes -> [], parameterHandleMode -> USE_JSON, pretty -> true, ignoreParamTypes -> []
    2023-06-11 12:37:36.388  INFO 31419 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    2023-06-11 12:37:36.407  INFO 31419 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2023-06-11 12:37:36.407  INFO 31419 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.58]
    2023-06-11 12:37:37.242  INFO 31419 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2023-06-11 12:37:37.243  INFO 31419 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 6875 ms
    2023-06-11 12:37:37.752  INFO 31419 --- [           main] c.i.c.aop.ParameterRecorderAdvice        : Set LocalVariableTableParameterNameDiscoverer as parameterNameDiscoverer.
    2023-06-11 12:37:39.695  INFO 31419 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2023-06-11 12:37:39.723  INFO 31419 --- [           main] com.ideaaedi.demo.StartUp                : Started StartUp in 11.118 seconds (JVM running for 12.13)
    2023-06-11 12:38:32.748  INFO 31419 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
    2023-06-11 12:38:32.748  INFO 31419 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
    2023-06-11 12:38:32.749  INFO 31419 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
    2023-06-11 12:38:32.796  INFO 31419 --- [nio-8080-exec-1] c.i.c.aop.ParameterRecorderAdvice        :
    // 这是aop工具记录的请求入参,说明1的请求进来了
    [the way in] request-path[] Class#Method -> com.ideaaedi.demo.controller.TestController#test, without any parameters
    // 程序接收到了优雅停机指令,Commencing graceful shutdown. Waiting for active requests to complete
    2023-06-11 12:38:34.593  INFO 31419 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
    2023-06-11 12:38:42.965  INFO 31419 --- [nio-8080-exec-1] c.i.c.aop.ParameterRecorderAdvice        :
    // 这是aop工具记录的请求出参,说明1的请求结束了
    [the way out] request-path[] Class#Method -> com.ideaaedi.demo.controller.TestController#test
            return type -> class java.lang.String
            return result -> "6o6cDmLi3n"
    // 优雅停机完成
    2023-06-11 12:38:43.033  INFO 31419 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown        : Graceful shutdown complete

    由此可见,优雅停机的目的达到了

相关资料

1
https://gitee.com/WY784755850/notebook.git
git@gitee.com:WY784755850/notebook.git
WY784755850
notebook
notebook
master

搜索帮助