前言
往往"停止服务"的代名词就是暴力,不计后果的,因为在强制停止的时候,不会管里面是否还有正在运行的线程。
碰巧最近由于在搞AWS的auto scalinng,不知道的朋友,可以把它理解为AWS可以自动的扩展或者是收缩我们的服务器,使得可以减少经费,想更深入了解的可以自行google。
这个出发点好是好,但是我也在实际使用的时候,发现了点问题:如果docker被stop了,里面可能存活的就被强制停止了,这个时候我么应该怎么办呢?
正文
根据
- docker stop命令实际上执行的是kill pid 指令,如果不跟随停止信号的话,默认情况下使用的是SIGNTEMR
- 并且如果docker中的主进程被停止,那么docker自然会停止。
所以推断问题的关键在于,我们需要去操控spring boot 需要优雅的stop,也就是我们今天的主角。
说了这么多废话该提起,下面进入正题,网上其实有很多这方面的教程例如说下面这个就写的很好:
https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html
但是网上的文档几乎都是把注入bean放在启动类中的,而我给它放在了@configuration 的类里,下面呢看下主要代码:
首先是最主要的监听容器关闭,并且进行处理的代码:
package com.demo.timeout.tomcat;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private volatile Connector connector;
private int waitTime;
@Value("${STOP_WAIT_TIMEOUT}")
public void setWaitTime(int waitTime) {
this.waitTime = waitTime;
}
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
try {
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
System.out.println("Tomcat 进程在" + waitTime + " 秒内无法结束,尝试强制结束");
}
}
} catch (Exception e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
然后是将其注入的代码:
package com.demo.timeout.config;
import com.demo.timeout.tomcat.GracefulShutdown;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GracefulShutDownConfig {
@Autowired
private GracefulShutdown gracefulShutdown;
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown);
return tomcatServletWebServerFactory;
}
}
最后写了一个API的测试代码
package com.demo.timeout.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/30s-process")
public String longProcessAPI(){
System.out.println("enter this long process function");
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "30s线程启动";
}
}
如果有需要可以看一下我上传的代码: