如何关闭一个Spring boot的应用?
方法对比
| actuator | 关闭上下文 | 退出应用 | PID杀死应用进程 | |
|---|---|---|---|---|
| 优点 | HTTP远程关闭 | 自定义清理工作 | 传递退出时INT值,方便JVM后续操作 | 灵活,甚至可以启动和重启应用 |
actuator
在pom文件中引入依赖以使用actuator.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
默认情况下,shutdown端点都是默认不开启的.可以通过在application.properties中添加以下代码:
management.endpoints.web.exposure.include=*
management.endpoint.shutdown.enabled=true
endpoints.shutdown.enabled=true
接下来通过http访问的方式,实现关闭spring boot 应用.
curl -X POST localhost:port/actuator/shutdown
关闭上下文
通过调用上下文的close()方法关闭上下文.
举个例子:
ConfigurableApplicationContext ctx = new
SpringApplicationBuilder(Application.class).web(WebApplicationType.NONE).run();
System.out.println("Spring Boot application started");
ctx.getBean(TerminateBean.class);
ctx.close();
close()方法会销毁所有的bean并释放锁,最后关闭所有的bean factory.
为了验证以上代码起到了想要的效果,使用spring生命周期容器被销毁前会调用的注解@PreDestroy来测试.
public class TerminateBean {
@PreDestroy
public void onDestroy() throws Exception {
System.out.println("Spring Container is destroyed!");
}
}
声明以下需要用到的bean
@Configuration
public class ShutdownConfig {
@Bean
public TerminateBean getTerminateBean() {
return new TerminateBean();
}
}
运行应用会得到如下日志:
17:01:20.542 [main] INFO com.baeldung.shutdown.Application - Started Application in 5.01 seconds (JVM running for 5.999)
Spring Boot application started
Spring Container is destroyed!
17:01:20.549 [main] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
17:01:20.549 [main] INFO o.h.t.s.i.SchemaDropperImpl$DelayedDropActionImpl - HHH000477: Starting delayed evictData of schema as part of SessionFactory shut-down'
17:01:20.552 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
17:01:20.556 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.
Process finished with exit code 0
ps:关闭上下文时,因为spring隔离的生命周期并不会影响父级上下文.
以上方法是创建了一个新的上下文并调用close()方法.如果想要关闭现在使用的context,最简单的方式就是使用上文的actuator的shutdown端点.当然也可以通过使用自定义的端点实现此功能.
@RestController
public class ShutdownController implements ApplicationContextAware {
private ApplicationContext context;
@PostMapping("/shutdownContext")
public void shutdownContext() {
((ConfigurableApplicationContext) context.close();
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.context = ctx;
}
}
实现ApplicationContextAware接口并重写setApplicationContext方法获得当前应用的context.
同样,通过http访问的方式,实现关闭spring boot应用.
curl -X POST localhost:port/shutdownContext
ps:对于自定义的endpoint,做好安全防护.
退出应用
spring 应用会在JVM中注册一个shutdown的钩子函数来确保退出恰当地退出应用.
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE).run();
int exitCode = SpringApplication.exit(ctx, new ExitCodeGenerator() {
@Override
public int getExitCode() {
// return the error code
return 0;
}
});
System.exit(exitCode);
调用system.exit(),会关闭JVM并返回INT值.
PID杀死应用进程
使用bash脚本在应用外杀死应用.
SpringApplicationBuilder app = new SpringApplicationBuilder(Application.class)
.web(WebApplicationType.NONE);
app.build().addListeners(new ApplicationPidFileWriter("./bin/shutdown.pid"));
app.run();
接着,创建一个shutdown.bat脚本:
kill -2 $(cat ./bin/shutdown.pid)
执行该脚本即可杀死应用.
gracefully shutdown
在Spring Boot 2.3 之后 ,支持全部四种web server的优雅关机,只需要在配置文件application.yml中加入
server:
shutdown: graceful
优雅关机的情况下,web server会停止接收新的请求.同时对于在优雅关机阶段进来的部分请求,server会等待一个时间段来完成这些请求.这个时间段同样可以通过配置设置,如下:
spring:
lifecycle:
timeout-per-shutdown-phase: 30s