How elegant close Spring Boot Application

How elegant close Spring Boot Application

Foreword

With the online application phase in SpringBoot building, SpringBoot increasingly multi-application example, when an application needs to be upgraded online deployment, often brutally simple using the kill command to stop the application of this way will make the application of all treatments discard request, failed to respond. Such responses fail, especially when dealing with important business logic needs to try to avoid, then what better way to smoothly close SpringBoot use it? Then this article's explore it. (This article is mainly for applications Spring Boot embedded Tomcat container as a Web-based service)

This article can be obtained by the sample code below Warehouse Address:

Environment Support:

  • JDK 8

  • SpringBoot 2.1.4

  • Maven 3.6.0

 

Custom Tomcat Connector behavior

To smooth off Spring Boot application first premise is that the built-in Web closed container, no outside process new incoming request. In order to allow the application to accept the closing event notification time, to ensure that the current Tomcat handles all incoming requests already, we need to implement TomcatConnectorCustomizer interface, the source interface is very simple, it can be seen from the comments this is to implement a custom Tomcat Connector behavior callback interface :

 

这里如果小伙伴对 Connector 不太熟悉,我就简单描述下: Connector 属于 Tomcat 抽象组件,功能就是用来接受外部请求,以及内部传递,并返回响应内容,是Tomcat 中请求处理和响应的重要组件,具体实现有 HTTP Connector 和 AJP Connector。

通过定制 Connector 的行为,我们就可以允许在请求处理完毕后进行 Tomcat 线程池的关闭,具体实现代码如下:

上述代码定义的 TIMEOUT 变量为 Tomcat 线程池延时关闭的最大等待时间,一旦超过这个时间就会强制关闭线程池,也就无法处理所有请求了,我们通过控制 Tomcat 线程池的关闭时机,来实现优雅关闭 Web 应用的功能。另外需要注意的是我们的类 CustomShutdown 实现了 ApplicationListener<ContextClosedEvent> 接口,意味着监听着 Spring 容器关闭的事件,即当前的 ApplicationContext 执行 close 方法。

内嵌 Tomcat 添加 Connector 回调

有了定制的 Connector 回调,我们需要在启动过程中添加到内嵌的 Tomcat 容器中,然后等待执行。那这一步又是如何实现的呢,可以参考下面代码:

这里的 TomcatServletWebServerFactory 是 Spring Boot 实现内嵌 Tomcat 的工厂类,类似的其他 Web 容器,也有对应的工厂类如 JettyServletWebServerFactory,UndertowServletWebServerFactory。他们共同的特点就是继承同个抽象类 AbstractServletWebServerFactory,提供了 Web 容器默认的公共实现,如应用上下文设置,会话管理等。

如果我们需要定义Spring Boot 内嵌的 Tomcat 容器时,就可以使用 TomcatServletWebServerFactory 来进行个性化定义,例如下方为官方文档提供自定示例:

好了说回正题,我们这里使用 addConnectorCustomizers 方法将自定义的 Connector 行为添加到内嵌的Tomcat 之上,为了查看加载效果,我们可以在 Spring Boot 程序启动后从容器中获取下webServerFactory 对象,然后观察,在它的 tomcatConnectorCustomizers 属性中可以看到已经有了 CustomeShutdown 对象。

 

开启 Shutdown Endpoint

到目前让内嵌 Tomcat 容器平稳关闭的操作已经完成,接下来要做的就是如何关闭主动关闭 Spring 容器了,除了常规Linux 命令 Kill,我们可以利用 Spring Boot Actuator 来实现Spring 容器的远程关闭,怎么实现继续看

Spring Boot Actuator 是 Spring Boot 的一大特性,它提供了丰富的功能来帮助我们监控和管理生产环境中运行的 Spring Boot 应用。我们可以通过 HTTP 或者 JMX 方式来对我们应用进行管理,除此之外,它为我们的应用提供了审计,健康状态和度量信息收集的功能,能帮助我们更全面地了解运行中的应用。

Actuator, ['æktʃʊˌeɪtə] 中文翻译过来就是制动器,这是一个制造业的术语,指的是用于控制某物的机械装置。

在 Spring Boot Actuator 中也提供控制应用关闭的功能,所以我们要为应用引入 Spring Boot Actuator,具体方式就是要将对应的 starter 依赖添加到当前项目中,以 Maven 项目为例:

Spring Boot Actuator 采用向外部暴露 Endpoint (端点)的方式来让我们与应用进行监控和管理,引入 spring-boot-starter-actuator 之后,我们就需要启用我们需要的 Shutdown Endpoint,在配置文件 application.properties 中,设置如下

第一行表示启用 Shutdown Endpoint ,第二行表示向外部以 HTTP 方式暴露所有 Endpoint,默认情况下除了 Shutdown Endpoint 之外,其他 Endpoint 都是启用的。

除了 Shutdown Endpoint,Actuator Endpoint 还有十余种,有的是特定操作,比如 heapdump 转储内存日志;有的是信息展示,比如 health 显示应用健康状态。具体所有 Endpoint 信息可以参见官方文档-53. Endpoints 一节。

到这里我们的前期配置工作就算完成了。当启动应用后,就可以通过POST 方式请求对应路径的 http://host:port/actuator/shutdown 来实现Spring Boot 应用远程关闭,是不是很简单呢。

模拟测试

这里为了模拟测试,我们首先模拟实现长达10s 时间处理业务的请求控制器 BusinessController,具体实现如下:

Thread.sleep 来阻塞当前请求线程,模拟业务处理,在此同时用 HTTP 方式访问 Shutdown Endpoint 试图关闭应用,可以通过观察控制台日志看是否应用是否会完成请求的处理后才真正进行关闭。

首先用 curl 命令模拟发送业务请求:

然后在业务处理中,直接发送请求 actuator/shutdown,尝试关闭应用,同样采用 curl 方式:

actuator/shutdown 请求发送后会立即返回响应结果,但应用并不会停止:

最后看下控制台的日志输出顺序:

可以看出在发送业务请求之后立刻发送关闭应用的请求,并不会立即将应用停止,而是在请求处理完毕之后,就是阻塞的 10s 后应用开始退出,这样可以保证已经接收到的请求能返回正常响应, 而关闭请求之后再进入的请求都不会被处理,到这里我们优雅关闭 Spring Boot 程序的操作就此实现了。

实现自动化

由于 Spring Boot 提供内嵌 Web 容器的便利性,我们经常将程序打包成 jar 然后发布。通常应用的启动和关闭操作流程是固定且重复的,本着 Don't Repeat Yourself 原则,我们有必要将这个操作过程自动化,将关闭和启用的 SpringBoot应用的操作写成 shell 脚本,以避免出现人为的差错,并且方便使用,提高操作效率。下面是我针对示例程序所写的程序启动脚本:(具体脚本可在示例项目查看)

有了脚本,我们可以直接通过命令行方式平滑地更新部署 Spring Boot 程序,效果如下:

总结

本文主要探究了如何对基于Spring Boot 内嵌 Tomcat 的 Web 应用进行平滑关闭的实现,如果采用其他 Web 容器也类似方式,希望这边文章有所帮助,若有错误或者不当之处,还请大家批评指正,一起学习交流。

参考

Guess you like

Origin www.cnblogs.com/one12138/p/11241274.html