Spring boot 2.3优雅下线,距离生产还有多远?


前言
在生产环境中 , 随着云原生架构的发展 , 自动的弹性伸缩、滚动升级、分批发布等云原生能力让用户享受到了资源、成本、稳定性的最优解 。 但是在应用的缩容、发布等过程中 , 由于实例下线处理得不够优雅 , 将会导致短暂的服务不可用 , 短时间内业务监控会出现大量 io 异常报错;如果业务没做好事务 , 那么还会引起数据不一致的问题 , 那么需要紧急手动订正错误数据;甚至每次发布 , 您需要发告示停机发布 , 否则您的用户会出现一段时间服务不可用 。 没处理好服务实例下线 , 无论发生上述哪种情况 , 都会对您业务的连续性造成困扰 。
对于任何一个线上应用 , 如何在服务更新部署过程中保证业务无感知是开发者必须要解决的问题 , 即从应用停止到重启恢复服务这个阶段不能影响正常的业务请求 , 这使得无损下线成为应用生命周期中必不可少的一个环节 。
同时在多次 Dubbo Meetup 中 , 平滑上下线一直都是位居微服务开发痛点前 Top 3 。
下面我们来了解一下 Spring Boot 2.3 中提供的新特性 Graceful Shutdown , 来分析一下它对我们生产稳定性带来什么样的帮助 。
Spring Boot graceful shutdown
Graceful shutdown
Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and Servlet-based web applications. When enabled using server.shutdown=graceful, upon shutdown, the web server will no longer permit new requests and will wait for a grace period for active requests to complete. The grace period can be configured using spring.lifecycle.timeout-per-shutdown-phase. Please see the reference documentation for further details.
Spring Boot 2.3.0.RELEASE引入了Graceful Shutdown的功能 。 其中应用在等待下线期间对待新请求的方式 , 取决于我们所使用的 Server 类型 。 根据官方文档Tomcat、Jetty 和 Reactor Netty将会在网络层面停止接收新的请求 。 Undertow 会继续接收新的请求 , 但立即会以 HTTP 503(服务不可用)来响应 。
配置与使用
在Spring Boot 2.3.0中 , 优雅停机的使用非常简单 , 可以通过在应用程序配置文件中设置两个属性来进行 。 1、 server.shutdown 属性可以支持的值有两种

  1. immediate 这是默认值 , 配置后服务器立即关闭 , 无优雅停机逻辑 。
  2. graceful 开启优雅停机功能 , 并遵守 spring.lifecycle.timeout-per-shutdown-phase 属性中给出的超时来作为服务端等待的最大时间 。 2、spring.lifecycle.timeout-per-shutdown-phase 服务端等待最大超时时间 , 采用java.time.Duration格式的值 , 默认30s 。
例如:Properties 文件
1、#To enable graceful shutdown
2、server.shutdown=graceful3、#To configure the timeout period4、spring.lifecycle.timeout-per-shutdown-phase=20s
当我们使用了如上配置开启了优雅停机功能 , 当我们通过SIGTERM信号关闭 Spring Boot 应用时1、 此时如果应用中没有正在进行的请求 , 应用程序将会直接关闭 , 而无需等待超时时间结束后才关闭 。 2、此时如果应用中有正在处理的请求 , 则应用程序将等待超时时间结束后才会关闭 。 如果应用在超时时间之后仍然有未处理完的请求 , 应用程序将抛出异常并继续强制关闭 。
源码实现分析
我们以 Tomcat 为例看一下是SpringBoot 2.3如何实现graceful shutdown的
这里注意下 , Tomcat 9.0.33或更高版本 , 才具备graceful shutdown功能 。
我们看一下 SpringBoot 的 TomcatWebServer 的实现 , 先看其中构造函数
1、org.springframework.boot.web.embedded.tomcat.TomcatWebServer