李成笔记网

专注域名、站长SEO知识分享与实战技巧

面试官:说说你们SpringBoot项目是如何优雅停机的?

环境:SpringBoot3.4.2



1. 为什么要优雅停机

  1. 数据完整性:在停机过程中,如果暴力终止进程,可能会导致事务执行中断或业务处理不完整,从而在数据库或系统中留下脏数据或残留文件。优雅停机能够确保在停机前完成所有正在处理的请求,并保存相关数据,保证数据的完整性和一致性。
  2. 用户体验:优雅停机能够在终止服务前,不再接受新的请求,但会完成已接收的请求,从而避免用户请求被中断或超时,提升用户体验。
  3. 资源释放:优雅停机能够确保在停机过程中,系统资源得到正确的释放,避免资源泄露或浪费。


2. 准备测试环境

准测试的环境,模拟耗时操作及资源销毁动作。

2.1 环境准备

@Component
public class PersonService {


  @PreDestroy
  public void destroy() {
    try {
      TimeUnit.SECONDS.sleep(5) ;
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("资源释放完成...") ;
  }
}

该Bean定义了销毁方法,容器在关闭时会执行。

@GetMapping("/index")
public Object index() {
  try {
    System.out.println("执行任务...") ;
    TimeUnit.SECONDS.sleep(30) ;
    System.out.println("任务执行完成...") ;
  } catch (InterruptedException e) {
    e.printStackTrace();
  }
  return "demo index" ;
}

该接口模拟耗时任务。主要用来观察发出停机信号后该接口是否会执行完成后再停机。

2.2 暴露停机

我们先来观察下暴露停机会是什么情况。

启动服务

请求/index接口

[root@pack ~]# curl http://localhost:8080/demos/index

强制终止进程

通过kill -9 强制终止进程

[root@pack ~]# jps
31713 Jps
26037 jar
[root@pack ~]# kill -9 26037
[root@pack ~]#

此时查看服务输出情况

请求的/index接口并没有执行完成,并且PersonService的销毁方法并没有执行。

3. 优雅停机方式

3.1 方式1

通过linux命令kill,但是这里我们不要添加-9 这样的参数。而是不是使用参数或者使用-15参数,示例如下:

[root@pack ~]# kill xxx  或者  kill -15 xxx

这里测试还是按照上面的方式进行,我们只需要观察服务控制台输出结果即可。

执行任务...
java.lang.InterruptedException: sleep interrupted
        at java.lang.Thread.sleep(Native Method)
资源释放完成...

当通过上面的方式终止进程后,PersonService的销毁方法执行完成以后程序才终止。确保资源的释放。为什么SpringBoot程序能正确的关闭呢?这是因为SpringBoot程序默认为JVM注册了一个关闭钩子,也就是添加了下面这样的代码

Runtime.getRuntime().addShutdownHook(...)

有兴趣可以查看下SpringApplication源码。

当我们配置了如下参数后,SpringBoot程序就算你使用kill xxx或者kill -15 xxx也会立即终止。

spring:
  main:
    register-shutdown-hook: false

关闭注册钩子。

提示:你也可以直接通过Ctrl + C的方式来终止效果与kill - 15 是一样的。

3.2 方式2

通过如下配置,开启优雅关闭

server:
  shutdown: graceful
---
spring:
  main:
    register-shutdown-hook: true

再次通过上面的方式进行测试,服务输出结果

当发出kill xxx信号后,我们请求的/index接口并没有直接终止,程序也没有立即终止而是等到所有的请求都结束以后才终止。这种优雅停机方式要比上一种优雅的太多了。

该种停机方式首先停止接收新的请求,然后等待所有正在执行的请求完成,最后关闭应用。

3.3 方式3

通过Actuator,Actuator提供了/shutdown接口,我们可以调用该接口进行程序的关闭。

引入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

默认情况下并没有开启shutdown接口,需要进行如下的配置

management:
  endpoints:
    web:
      base-path: /ac
    exposure:
      include: '*'
  endpoint:
    shutdown:
      enabled: true

接下来测试方式与上面相同,只不过停止服务需要通过如下接口

http://localhost:8080/ac/shutdown

当执行上面接口后

服务输出结果

服务与上面一样优雅的停机等待当前正处理的请求结束后再停机。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言