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

爱写Bug的麦洛
• 阅读 1758

前言

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 秒的默认宽限期内完成正在进行的请求。但是,我们可以更改此设置以指定自定义超时值或宽限期。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
基于mysqldump聊一聊MySQL的备份和恢复
前言Hi,大家好,我是麦洛,今天我们聊聊MySQL的备份和恢复,在下面文章中,你会了解到MySQL常见的备份类型,以及基于mysqldump命令在日常开发中如何做
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
3年前
JOptionPane修改图标
1.在Linux平台下.JOptionPane会显示Java默认的图标,在window平台不显示图标,如何替换这个图标了?2JOptionPane.setIcon(Icon)修改的是内容区域的icon,而不是左上角的Icon.所以需要通过修改Jdialog/Frame的图标来达到修改默认图标的问题.3.代码:if(JOptio
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这