如何优雅地关闭SpringBoot应用程序?听我给你讲

麦洛 等级 1210 1 0
标签: boot

前言

Hi,大家好,我是麦洛,今天来聊聊如何优雅地关闭SpringBoot应用程序,有需要交流的朋友,可以私信我或者加我微信:miloleex都可以哈

在我们日常开发中,我们如何启停服务?可能下面的命令在熟悉不过了。

ps -ef|grep 8888
kill -9 8888
./startup.sh ;tail -f ../logs/catalina.out

暴利美学式的启停服务真的安全吗?今天我们来了解如何安全、优雅地停止 Spring Boot 应用程序,而不会使任何当前处理的请求失败或不中断正在进行的事务。

什么是正常关机?

优雅和强制关闭是停止应用程序的两种方法。当应用程序运行时,在处理请求的过程中。我们都知道它会消耗各种资源、建立连接、持久化数据或处理事务等。当我们修复了某个bug或者需要发版时,就需要我们停止当前正在运行的应用程序,以便完成我们的工作。这时候,采取合适的手段就显得尤为重要。

为了说明这个现象,我们来举一个例子。我们生活中关闭计算机时。如果遇到未保存的文件或一些程序未关闭,windows会提示我们关机之前完成这些工作。如果我们强制关机,就会丢失一些我们想要的东西,同样的逻辑也适用于我们的应用程序。幸运的是,某些应用程序能够在重新启动时可以恢复任何未完成的任务。这样停止不会造成任何伤害。另一方面,对于某些应用程序,突然关闭可能会导致意外结果。例如,非常大的事务失败,或打开资源等。那么Spring Boot 应用程序我们如何优雅的关机,接下来我们一起来看看,微信搜一搜"爱写Bug的麦洛"公众号关注我,一起努力写Bug,哈哈

未启用正常关机

当我们停止正在运行的应用程序或进程时,底层操作系统会向进程发送终止信号。在没有启用任何优雅关闭机制的情况下,Spring Boot 应用程序将在收到信号后立即终止。

为了演示这种行为,让我们编写一个 Controller 端点,它在返回之前需要更长的时间。或者,我们可以简单地让线程休眠相当长的时间,以便我们有机会在请求处理过程中停止应用程序。

@PostMapping("/students")
public void test(@RequestBody Student student) {
    log.info("新请求新增一个学生");
    studentService.addStudent(student);
    log.info("新增学生成功");
}

首先,我们将启动应用程序并向该端点执行请求。之后,一旦请求开始处理,我们将尝试停止应用程序。最后,我们将观察日志。

11:04:44|INFO | o.s.w.s.DispatcherServlet:547 - Completed initialization in 33 ms
11:04:44|INFO | c.a.s.t.s.w.StudentController:38 - 新请求新增一个学生
11:04:53|INFO | o.s.s.c.ThreadPoolTaskExecutor:218 - Shutting down ExecutorService 'applicationTaskExecutor'
11:04:53|INFO | c.z.h.HikariDataSource:350 - HikariPool-1 - Shutdown initiated...
11:04:53|INFO | c.z.h.HikariDataSource:352 - HikariPool-1 - Shutdown completed.

日志表明在 POST 请求完成之前,我们尝试关闭应用程序。从日志中可以明显看出,应用程序在没有等待请求完成就突然停止。

启用正常关机

Spring Boot 支持自动配置的优雅关机,配置非常简单。下一个配置片段显示了如何在 Spring Boot 中启用优雅关闭。

yaml文件:

server:
  shutdown: graceful

属性文件:

server.shutdown=graceful

启用此功能后,Spring Boot 将在完全关闭应用程序上下文之前等待当前请求完成。此外,在关闭阶段,它将停止接受新请求。Spring Boot 的所有嵌入式服务器都支持优雅终止。但是,拒绝新请求的方式可能会因各个服务器的实现而异。

这里演示如何在停止 Spring Boot 应用程序之前允许当前正在运行的 HTTP 请求完成。现在我们已经启用了正常关闭,我们将重新运行控制器端点并尝试在请求完成之前停止应用程序。

@PostMapping("/students")
public void test(@RequestBody Student student) {
    log.info("新请求新增一个学生");
    studentService.addStudent(student);
    log.info("新增学生成功");
}

让我们执行 POST 端点,并立即停止应用程序

14:14:57|INFO | c.a.s.t.s.w.StudentController:38 - 新请求新增一个学生
14:14:58|INFO | o.s.b.w.e.t.GracefulShutdown:53 - Commencing graceful shutdown. Waiting for active requests to complete
14:15:07|INFO | c.a.s.t.s.w.StudentController:40 - 新增学生成功
14:15:07|INFO | o.s.b.w.e.t.GracefulShutdown:78 - Graceful shutdown complete
14:15:07|INFO | o.s.s.c.ThreadPoolTaskExecutor:218 - Shutting down ExecutorService 'applicationTaskExecutor'
14:15:07|INFO | c.z.h.HikariDataSource:350 - HikariPool-1 - Shutdown initiated...
14:15:07|INFO | c.z.h.HikariDataSource:352 - HikariPool-1 - Shutdown completed.

从日志中可以看出,在调用后立即收到了关闭信号。但是,Spring Boot 仍然允许在完全停止应用程序上下文之前完成请求。

正常关机超时

在正常关闭期间,Spring Boot 允许应用程序在一些宽限期内完成所有当前请求或进程。一旦宽限期结束,未完成的进程或请求就会被终止。默认情况下,Spring Boot 允许 30 秒的正常关闭超时。但是,我们可以使用应用程序属性或 Yaml 文件对其进行配置。

yaml文件:

spring:
  lifecycle:
    timeout-per-shutdown-phase: "10s"

属性文件:

spring.lifecycle.timeout-per-shutdown-phase=10s

不同嵌入式 Web 服务器优雅停机行为区别?

容器停机行为取决于具体的 web 容器行为

web 容器名称 行为说明
Tomcat 9.0.33+ 停止接受网络层的请求,客户端新请求等待超时。
Reactor Netty 停止接受网络层的请求,客户端新请求等待超时。
Undertow 接受请求,客户端新请求直接返回 503。
Jetty 停止接受网络层的请求,客户端新请求等待超时。

特别说明

官网中有这样一段说明,如果你的IDE无法正确发送SIGTERM信号,可能无法在本地测试

如何优雅地关闭SpringBoot应用程序?听我给你讲

总结

在上文中,我们学习了如何优雅地关闭Spring Boot 应用程序。配置启用正常关闭后,Spring Boot 允许应用程序在完全停止应用程序之前完成任何正在进行的请求或进程。此外,它可以防止任何新请求到达应用程序。而且允许在 30 秒的默认宽限期内完成正在进行的请求。但是,我们可以更改此设置以指定自定义超时值或宽限期。

收藏
评论区

相关推荐

SpringBoot 2.0 系列001
SpringBoot 2.0 系列001 -- 入门介绍以及相关概念 ================================== 什么是SpringBoot? -------------- 项目地址:[http://projects.spring.io/spring-boot/](https://www.oschina.net/action/G
SpringBoot 整合Filter
SpringBoot 整合Filter =================== 一、创建基础SpringBoot项目,集成Web即可 -------------------------- <**dependency**\> <**groupId**\>org.springframework.boot</**groupId**\> <**a
SpringBoot2.0 基础案例(17):自定义启动页,项目打包和指定运行环境
本文源码 GitHub地址:知了一笑 https://github.com/cicadasmile/spring-boot-base 一、打包简介 ====== springboot的打包方式有很多种。可以打war包,可以打jar包,可以使用jekins进行打包部署的。不推荐用war包,SpringBoot适合前后端分离
SpringBoot2.x基础篇:带你了解扫描Package自动注册Bean
![](https://oscimg.oschina.net/oscnet/up-6d27eb9793200379ece2b49fc26ed46ef74.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](http
SpringBoot2.x基础篇:应用程序在启动时访问启动项参数
![](https://oscimg.oschina.net/oscnet/up-0fc757b29ab18ad630d891e2e83eb81fea4.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](http
SpringBoot2.x基础篇:探索配置文件中随机数的实现方式
![](https://upload-images.jianshu.io/upload_images/4461954-ed8cb2c7536b87f3.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](https
SpringBoot2.x基础篇:灵活的使用外部化配置信息
![](https://oscimg.oschina.net/oscnet/up-9b884e3dd97b57725c09c1f5aff2607ab8d.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](http
SpringBoot2.x基础篇:配置文件的加载顺序以及优先级覆盖
![](https://upload-images.jianshu.io/upload_images/4461954-ed8cb2c7536b87f3.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](https
SpringBoot2.x基础篇:灵活的使用外部化配置信息
![](https://oscimg.oschina.net/oscnet/up-9b884e3dd97b57725c09c1f5aff2607ab8d.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](http
SpringBoot2.x基础篇:配置文件的加载顺序以及优先级覆盖
![](https://upload-images.jianshu.io/upload_images/4461954-ed8cb2c7536b87f3.png) > 知识改变命运,撸码使我快乐,2020继续游走在开源界<br/> 点赞再看,养成习惯<br/> 给我来个Star吧,[点击了解下基于SpringBoot的组件化接口服务落地解决方案](https
Springboot框架实现图片上传显示并保存地址到数据库
1.在application.properties.xml中配置 -------------------------------- # SpringBoot框架实现图片上传显示并保存地址到数据库#spring boot上传文件大小限制spring.http.multipart.max-file-size=200MBspring.http.multi
springboot2中使用dubbo的三重境界
在springboot中使用dubbo,本来是件挺简单的事情,但现实的世界就是如此的复杂,今天我用一个亲身经历的跳坑和填坑的事来讲在spring boot中使用高版本dubbo(当当的魔改版)的三重境界。 1、看山是山,使用官方starter ================== 简单的使用dubbo starter集成进spring boot还是非常简
springboot如何集成Prometheus如何暴露Histogram来获取P99等监控指标
背景 == springboot如何集成Prometheus我这里不做详细描述,要想了解集成过程,可以参考一下博客: [Spring Boot 使用 Micrometer 集成 Prometheus 监控 Java 应用性能](https://www.oschina.net/action/GoToLink?url=https%3A%2F%2Fblog.cs
springboot整合javafx
原文(原码)参考地址:   https://github.com/roskenet/springboot-javafx-support   https://github.com/spartajet/javafx-boot-demo   https://blog.spartajet.com/javafx-springboot-maven-kai-fa-d
tio
> 本文讲述如何快速将 tio 服务整合到 SpringBoot 项目 首先,你需要在 pom.xml 中引入 `tio-core-spring-boot-starter` 构件 <dependency> <groupId>org.t-io</groupId> <artifactId>tio-core-spring-boot-