0%

Spring Boot 2.3.0 优雅关机

5月15日,Spring Boot 2.3.0正式发布,其提供了原生的优雅关机(Graceful shutdown)的功能,摆脱了第三方优雅关机库的依赖,我们来简单看看其使用姿势以及原理。

用法

pom文件中指定2.3.0版本的依赖:

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/>
</parent>

在.yml配置文件中,开启优雅关机:

1
2
3
server:
port: 12345
shutdown: # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机

然后我们写一个Controller测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
try {
Thread.sleep(20 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "hello spring boot 2.3.0";
}
}

hello()方法接收到请求之后,会sleep 20秒,然后再返回结果,我们先将server.shutdown设置为IMMEDIATE,看看是什么情况:

应用在收到kill -2发送的中断信号之后,输出如下异常日志,并且http请求没有正常返回结果:

1
2
3
4
5
6
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at fun.cllc.demo.HelloController.hello(HelloController.java:15)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at ...

将server.shutdown设置GRACEFUL,即是开启优雅关机,再次尝试,应用输出如下:

1
2
3
2020-05-25 11:00:52.840  INFO 83658 --- [extShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown        : Commencing graceful shutdown. Waiting for active requests to complete
2020-05-25 11:01:07.305 INFO 83658 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
2020-05-25 11:01:07.314 INFO 83658 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
  • 应用收到kill -2发送的中断信号之后,打印日志,提示开始优雅关机,并且等待当前请求结束;
  • 所有请求处理结束之后,执行关机;
  • 并且http请求能够正常返回“hello spring boot 2.3.0”。

原理

Spring Boot默认使用的是tomcat,初始化TomcatWebServer的时候,会根据配置创建gracefulShutdown成员变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TomcatWebServer implements WebServer {
private final GracefulShutdown gracefulShutdown;

public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
initialize();
}

@Override
public void shutDownGracefully(GracefulShutdownCallback callback) {
if (this.gracefulShutdown == null) {
callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
return;
}
this.gracefulShutdown.shutDownGracefully(callback);
}
}

如果server.shutdown配置的是GRACEFUL,则会创建一个GracefulShutdown对象。当应用收到中断请求时,会调用shutDownGracefully函数,如果配置的是优雅关机,则会进入到GracefulShutdown#shutDownGracefully中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
final class GracefulShutdown {
void shutDownGracefully(GracefulShutdownCallback callback) {
logger.info("Commencing graceful shutdown. Waiting for active requests to complete");
new Thread(() -> doShutdown(callback), "tomcat-shutdown").start();
}

private void doShutdown(GracefulShutdownCallback callback) {
List<Connector> connectors = getConnectors();
connectors.forEach(this::close);
try {
for (Container host : this.tomcat.getEngine().findChildren()) {
for (Container context : host.findChildren()) {
while (isActive(context)) {
if (this.aborted) {
logger.info("Graceful shutdown aborted with one or more requests still active");
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
return;
}
Thread.sleep(50);
}
}
}
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
logger.info("Graceful shutdown complete");
callback.shutdownComplete(GracefulShutdownResult.IDLE);
}

// 获取所有连接
private List<Connector> getConnectors() {
List<Connector> connectors = new ArrayList<>();
for (Service service : this.tomcat.getServer().findServices()) {
Collections.addAll(connectors, service.findConnectors());
}
return connectors;
}

// 关闭某个连接
private void close(Connector connector) {
// 暂停Connector,暂停接收新的请求
connector.pause();
// 优雅关闭Connector。即是等待请求处理完毕之后再关闭
connector.getProtocolHandler().closeServerSocketGraceful();
}

private boolean isActive(Container context) {
try {
if (((StandardContext) context).getInProgressAsyncCount() > 0) {
return true;
}
for (Container wrapper : context.findChildren()) {
if (((StandardWrapper) wrapper).getCountAllocated() > 0) {
return true;
}
}
return false;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}

在GracefulShutdown中,主要干了如下几个事情:

  • 获取所有连接;
  • 关闭连接:先暂停连接,以停止接收新的请求,然后关闭连接;
  • 等待请求处理完成:使用while+sleep循环判断;
  • 调用callback回调。



-=全文完=-