springboot启动一下就停止了_SpringBoot 优雅停止服务的几种方法 - 第309篇

78c496cc4fe745be235f9ebc8a8a38b6.png

相关历史文章(阅读本文之前,您可能需要先看下之前的系列 )

国内最全的Spring Boot系列之三
一分钟get:缓存穿透、缓存击穿、缓存雪崩 - 第304篇
布隆过滤器Bloom Filter竟然让我解决了一个大厂的问题 - 第305篇
100G的文件如何读取 - 第306篇
100G的文件如何读取续集 - 第307篇
Java语言的优雅停机 - 第308篇

师傅:对于java的shutdown hook有所了解之后,我们还是重点来看看Spring Boot如何优化停止服务吧。

悟纤:师傅,这节课那我认真听了,不然到时候生产环境都不知道如何正确停止服务了。

cedf1ccee9a61716237440bc2df32b03.png

师傅:你这话… 说的… 师傅讲的每节课都很重要,好不。

悟纤:师傅,每节课我都有认真听的,不行待会你随便考我下。

42cb99f86c42c0f4a2c566427a83a1f5.png

师傅:那我们今天先把这个SpringBoot如何优雅停止学习完,在慢慢折磨你…

悟纤:师傅,我脑瓜怎么就嗡嗡的呐。

bb3be25f7643829b58741ef95c3802fe.png

一、准备工作

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关闭。

32153d5ed80cbaf2a50ac42f288fc146.png

此时控制台不会执行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);

我们看下源码:

@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方法。

三、悟纤小结

悟纤:师傅,你今天这节课讲的有点乱呐?是不是来事了?

a8f3bb1728b67b16ae3c2f28ccab67d6.png

师傅:徒儿,看来你今天没有挨抽,不舒服,是吧?

80f05a8e5f458dfd80e38e1345afe90c.png

悟纤:师傅,那怪徒儿太笨,没消化的了嘛。

4b779797793852c57f4d60d2b56b5529.png

师傅:那好,你按照自己的理解,来和大家梳理下。

bdc3ea1baf7dec2eac57c0b181e596f0.png

小结下:

一、通过应用暴露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强制关闭进程了。

我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。
学院中有 Spring Boot相关的课程:
à悟空学院: https:// t.cn/Rg3fKJD
SpringBoot视频: http:// t.cn/A6ZagYTi
Spring Cloud视频: http:// t.cn/A6ZagxSR
SpringBoot Shiro视频: http:// t.cn/A6Zag7IV
SpringBoot交流平台: https:// t.cn/R3QDhU0
SpringData和JPA视频: http:// t.cn/A6Zad1OH
SpringSecurity5.0视频: http:// t.cn/A6ZadMBe
Sharding-JDBC分库分表实战: http:// t.cn/A6ZarrqS
分布式事务解决方案「手写代码」: http:// t.cn/A6ZaBnIr
JVM内存模型和性能调优: http:// t.cn/A6wWMVqG
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值