优雅停机
主要应用在版本更新的时候,为了等待正在工作的线程全部执行完毕,然后再停止
2.3及以上版本
1.maven引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.修改配置文件
management:
endpoint:
shutdown:
enabled: true
endpoints:
web:
exposure:
include: "shutdown"
base-path: /monitor
2.1版本
除以上两步外尚需修改Application启动类
tomcat容器
package com.example;
import org.apache.catalina.connector.Connector;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.ContextClosedEvent;
import com.example.framework.datasource.DynamicDataSourceContextHolder;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 启动程序
*/
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
@MapperScan("com.example.*.mapper")
//@EnableScheduling
public class NumberoneApplication
{
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
public static void main(String[] args)
{
// System.setProperty("spring.devtools.restart.enabled", "false");
SpringApplication.run(NumberoneApplication.class, args);
log.info("----- Start Successful~启动成功 -----");
}
/**
* 用于接受 shutdown 事件
*/
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
/**
* 配置tomcat
*
* @return
*/
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(gracefulShutdown());
return tomcat;
}
/**
* 优雅关闭 Spring Boot。容器必须是 tomcat
*/
private class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private volatile Connector connector;
private final int waitTime = 10;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
log.warn("Tomcat 进程在" + waitTime + " 秒内无法结束,尝试强制结束");
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
}
Undertow容器
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
/**
* 优雅关闭 Spring Boot
*/
@Component
public class GracefulShutdown implements ApplicationListener<ContextClosedEvent> {
@Autowired
private GracefulShutdownWrapper gracefulShutdownWrapper;
@Autowired
private ServletWebServerApplicationContext context;
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent){
gracefulShutdownWrapper.getGracefulShutdownHandler().shutdown();
try {
UndertowServletWebServer webServer = (UndertowServletWebServer)context.getWebServer();
Field field = webServer.getClass().getDeclaredField("undertow");
field.setAccessible(true);
Undertow undertow = (Undertow) field.get(webServer);
List<Undertow.ListenerInfo> listenerInfo = undertow.getListenerInfo();
Undertow.ListenerInfo listener = listenerInfo.get(0);
ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();
while (connectorStatistics.getActiveConnections() > 0){}
}catch (Exception e){
// Application Shutdown
}
}
}
}
测试验证
测试
通过postman或curl -X POST http://localhost:8080/*/shutdown
发送请求验证
control内添加方法
@RequestMapping(value = "/a/testClose",method= RequestMethod.GET)
@ResponseBody
public String sayHi() throws InterruptedException {
// 模拟复杂业务耗时处理流程
Thread.sleep(10 * 1000L);
return "success";
}
启动项目,使用postman测试 ,会等待10s后显示请求成功,等待中…:
验证
2.1.1版本 Tomcat容器验证成功(无论是通过通过post方式调用接口或是程序命令窗口ctrl+c退出均优雅关闭程序)
参考网址
Spring boot 2.0 之优雅停机()
Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现
Spring Boot 2.0 实现优雅停机
再谈spring boot 优雅停机
使用actuator优雅地停止SpringBoot应用
curl 命令模拟 HTTP GET/POST 请求
Windows使用curl发送GET、POST请求