回复“资源”获取独家整理的学习资料! 前言随着线上应用逐步采用 SpringBoot 构建,SpringBoot应用实例越来多,当线上某个应用需要升级部署时,常常简单粗暴地使用 kill 命令,这种停止应用的方式会让应用将所有处理中的请求丢弃,响应失败。这样的响应失败尤其是在处理重要业务逻辑时需要极力避免的,那么有什么更好的方式来平滑地关闭 SpringBoot 应用呢?那就通过本文一起来探究吧。(本文主要针对基于Spring Boot 内嵌 Tomcat 容器作为 Web 服务的应用)
定制 Tomcat Connector 行为要平滑关闭 Spring Boot 应用的前提就是首先要关闭其内置的 Web 容器,不再处理外部新进入的请求。为了能让应用接受关闭事件通知的时候,保证当前 Tomcat 处理所有已经进入的请求,我们需要实现 TomcatConnectorCustomizer 接口,这个接口的源码十分简单,从注释可以看出这是实现自定义 Tomcat Connector 行为的回调接口: 这里如果小伙伴对 Connector 不太熟悉,我就简单描述下:Connector 属于 Tomcat 抽象组件,功能就是用来接受外部请求,以及内部传递,并返回响应内容,是Tomcat 中请求处理和响应的重要组件,具体实现有 HTTP Connector 和 AJP Connector。 通过定制 Connector 的行为,我们就可以允许在请求处理完毕后进行 Tomcat 线程池的关闭,具体实现代码如下: 上述代码定义的 TIMEOUT 变量为 Tomcat 线程池延时关闭的最大等待时间,一旦超过这个时间就会强制关闭线程池,也就无法处理所有请求了,我们通过控制 Tomcat 线程池的关闭时机,来实现优雅关闭 Web 应用的功能。另外需要注意的是我们的类 CustomShutdown 实现了 ApplicationListener 内嵌 Tomcat 添加 Connector 回调有了定制的 Connector 回调,我们需要在启动过程中添加到内嵌的 Tomcat 容器中,然后等待执行。那这一步又是如何实现的呢,可以参考下面代码: 这里的 TomcatServletWebServerFactory 是 Spring Boot 实现内嵌 Tomcat 的工厂类,类似的其他 Web 容器,也有对应的工厂类如 JettyServletWebServerFactory,UndertowServletWebServerFactory。他们共同的特点就是继承同个抽象类 AbstractServletWebServerFactory,提供了 Web 容器默认的公共实现,如应用上下文设置,会话管理等。 如果我们需要定义Spring Boot 内嵌的 Tomcat 容器时,就可以使用 TomcatServletWebServerFactory 来进行个性化定义,例如下方为官方文档提供自定示例: 好了说回正题,我们这里使用 开启 Shutdown Endpoint到目前让内嵌 Tomcat 容器平稳关闭的操作已经完成,接下来要做的就是如何关闭主动关闭 Spring 容器了,除了常规Linux 命令 Kill,我们可以利用 Spring Boot Actuator 来实现Spring 容器的远程关闭,怎么实现继续看 Spring Boot Actuator 是 Spring Boot 的一大特性,它提供了丰富的功能来帮助我们监控和管理生产环境中运行的 Spring Boot 应用。我们可以通过 HTTP 或者 JMX 方式来对我们应用进行管理,除此之外,它为我们的应用提供了审计,健康状态和度量信息收集的功能,能帮助我们更全面地了解运行中的应用。
在 Spring Boot Actuator 中也提供控制应用关闭的功能,所以我们要为应用引入 Spring Boot Actuator,具体方式就是要将对应的 starter 依赖添加到当前项目中,以 Maven 项目为例: Spring Boot Actuator 采用向外部暴露 Endpoint (端点)的方式来让我们与应用进行监控和管理,引入 第一行表示启用 Shutdown Endpoint ,第二行表示向外部以 HTTP 方式暴露所有 Endpoint,默认情况下除了 Shutdown Endpoint 之外,其他 Endpoint 都是启用的。
到这里我们的前期配置工作就算完成了。当启动应用后,就可以通过POST 方式请求对应路径的 模拟测试这里为了模拟测试,我们首先模拟实现长达10s 时间处理业务的请求控制器 BusinessController,具体实现如下: 用 首先用 curl 命令模拟发送业务请求: 然后在业务处理中,直接发送请求
最后看下控制台的日志输出顺序: 可以看出在发送业务请求之后立刻发送关闭应用的请求,并不会立即将应用停止,而是在请求处理完毕之后,就是阻塞的 10s 后应用开始退出,这样可以保证已经接收到的请求能返回正常响应, 而关闭请求之后再进入的请求都不会被处理,到这里我们优雅关闭 Spring Boot 程序的操作就此实现了。 实现自动化由于 Spring Boot 提供内嵌 Web 容器的便利性,我们经常将程序打包成 jar 然后发布。通常应用的启动和关闭操作流程是固定且重复的,本着 Don't Repeat Yourself 原则,我们有必要将这个操作过程自动化,将关闭和启用的 SpringBoot应用的操作写成 shell 脚本,以避免出现人为的差错,并且方便使用,提高操作效率。下面是我针对示例程序所写的程序启动脚本:(具体脚本可在示例项目查看) 有了脚本,我们可以直接通过命令行方式平滑地更新部署 Spring Boot 程序,效果如下: 总结本文主要探究了如何对基于Spring Boot 内嵌 Tomcat 的 Web 应用进行平滑关闭的实现,如果采用其他 Web 容器也类似方式,希望这边文章有所帮助,若有错误或者不当之处,还请大家批评指正,一起学习交流。 |
|
来自: liang1234_ > 《springboot》