SpringBoot 优雅停止服务的几种方法 - 第309篇
一、准备工作
1.1 准备一个项目
随便创建一个Spring Boot项目,这里使用的是截止到2020年5月 最新的版本:2.2.7.RELEASE。
1.2 创建一个bean
我们创建一个Bean ,有一个销毁的方法:
- @Component
- public class TerminateBean {
-
- @PreDestroy
- public void preDestroy() {
- System.out.println("TerminalBean is destroyed");
- }
-
- }
在接下来的测试中,主要是看这个preDestroy会不会被执行。
1.3 添加actuator的功能
SpringBoot 提供的actuator的功能,它可以执行shutdown, health, info等,默认情况下,actuator的shutdown是disable的,我们需要打开它。
利用actuator也是停止服务的方式,在下面会使用到,这里先把依赖添加进来。
引入acturator的maven依赖:
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-actuator</artifactId>
- </dependency>
打开shutdown节点:
修改applicatoin.properties文件:
- management.endpoint.shutdown.enabled=true
- management.endpoints.web.exposure.include=*
这里暴露了所有的,也可以指定暴露shutdown:
- management.endpoint.shutdown.enabled=true
- management.endpoints.web.exposure.include=shutdown
二、SpringBoot的n中停机操作
2.1 Run As – Java Application运行 – Console Termiante关闭
我们使用Run As – Java Application的方式进行运行我们的Application,启动成功之后,使用Console中的Terminate关闭。
此时控制台不会执行preDestroy方法。
2.2 Run As – Spring Boot App运行 – Console Terminate关闭
我们使用Run As – Spring Boot App的方式进行运行我们的Application,启动成功之后,使用Console中的Terminate关闭。
此时控制台会执行preDestroy方法。
2.3 Run As – Java Application运行 – kill -15 pid关闭
我们使用Run As – Java Application的方式进行运行我们的Application,启动成功之后,kill -15 pid。
此时控制台会执行preDestroy方法。
- 2020-05-09 19:47:08.640 INFO 682 --- [extShutdownHook] o.s.s.concurrent.ThreadPoolTaskExecutor : Shutting down ExecutorService 'applicationTaskExecutor'
- TerminalBean is destroyed
注意控制台的打印信息:有一个我们在前面讲过的非常熟悉的extShutdownHook,不然猜出Spring底层也是使用了
Runtime.getRuntime().addShutdownHook(Thread hook);
我们看下源码:
org.springframework.context.support.AbstractApplicationContext.registerShutdownHook() :
- @Override
- public void registerShutdownHook() {
- if (this.shutdownHook == null) {
- // No shutdown hook registered yet.
- this.shutdownHook = new Thread() {
- @Override
- public void run() {
- synchronized (startupShutdownMonitor) {
- doClose();
- }
- }
- };
- Runtime.getRuntime().addShutdownHook(this.shutdownHook);
- }
- }
2.4 Run As – Java Application运行 – kill -9 pid关闭
我们使用Run As – Java Application的方式进行运行我们的Application,启动成功之后,kill -9 pid。
此时控制台不会执行preDestroy方法。
2.5 actuator:post shutdown
还记得我们在准备工作的时候,添加了actuator,这个会暴露出来一个/actuator/shutdown的地址,我们就可以请求一下:
curl -X POST http://localhost:8080/actuator/shutdown
注意:是post方法,而且post这里要大写、大写、大写,重要的事情说3遍,否则会报错:
- {
- "timestamp": "2020-05-11T07:37:55.154+0000",
- "status": 405,
- "error": "Method Not Allowed",
- "message": "Request method 'post' not supported",
- "path": "/actuator/shutdown"
- }
请求成功的话,会返回:
{"message":"Shutting down, bye..."}
此时控制台会执行preDestroy方法。
2.6 ApplicationContext.close()
这种方式的思路就是获取到ApplicationContext,然后调用它的close()方法。
- @RestController
- public class ShutDownController implements ApplicationContextAware {
-
- private ApplicationContext applicationContext;
-
- @Override
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- this.applicationContext = applicationContext;
- }
-
- @RequestMapping("/shutdownByCTX")
- public String shutdownByCTX(){
- ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) applicationContext;
- ctx.close();
- return "context is shutdown!!!";
- }
-
- }
说明:编写一个类实现ApplicationContextAware,主要是要获取上下文ApplicationContext;然后我们暴露一个请求/shutdownByCTX,在此方法使用applicationContext的close方法。
测试,可以通过浏览器访问地址:
http://127.0.0.1:8080/shutdownByCTX
注意:当请求/shutdownByCTX的话,并不能看到返回值,而是:
curl: (52) Empty reply from server
此时控制台会执行preDestroy方法。
2.7 SpringApplication.exit()
利用SpringApplication提供的exit方法:
- @RequestMapping("/shutdownBySpringApplication")
- public String shutdownBySpringApplication(){
- SpringApplication.exit(applicationContext,(ExitCodeGenerator)()->0);
- return "context is shutdown!!!";
- }
此时控制台会执行preDestroy方法。
2.8 pid | xargs kill
此方式是将pid写入到文件,然后使用xargs kill进行关闭服务。
- public static void main(String[] args) {
- SpringApplication application = new SpringApplication(SpringBootShutdownDemoApplication.class);
-
- // 指定一个文件,写入pid号
- application.addListeners(new ApplicationPidFileWriter("/data/tmp/app.pid"));
- application.run(args);
- }
通过命令 cat /data/tmp/app.pid | xargs kill 命令直接停止服务。
此时控制台会执行preDestroy方法。
小结下:
一、通过应用暴露shutdown
(1)actutor的/actutor/shutdown方法(需要配置):此时控制台会执行preDestroy方法。
(2)利用Application的close()方法(需要编码暴露出一个访问的方法):此时控制台会执行preDestroy方法。
(3)利用SpingApplication的exit()方法(需要编码暴露出一个访问的方法):此时控制台会执行preDestroy方法。
二、kill
(1)kill -9 pid:不需要编码,也不需要配置,利用操作系统的强制关闭经常指令;此时控制台不会执行preDestroy方法。
(2)kill -15 pid:不需要编码,也不需要配置,利用操作系统的强制关闭经常指令;此时控制台会执行preDestroy方法。
(3)kill | xargs kill:需要编码,然后利用操作系统的指令cat /data/tmp/app.pid | xargs kill;此时控制台会执行preDestroy方法。
四、生产环境怎么玩呐?
在生产环境,我们是这么玩的?
编写一个sh脚本,通过grep查找到我们项目的pid,然后先使用kill -15 pid,然后sleep一下,然后在超找pid , 如果没找到,说明已经关闭了,如果找到了,说明关闭失败了,那么就使用kill -9 pid强制关闭进程了。
- 我就是我,是颜色不一样的烟火。
- 我就是我,是与众不同的小苹果。
购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287