boot版本1.5.8
一、原始方法:boot自带的shutdown
引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
设置
# POST 方法
endpoints:
shutdown:
enabled: true
#禁用密码验证
sensitive: false
使用post访问/shutdown 可以关闭容器
二、进阶版本
如果有线程池在使用以上的方式/shutdown会报错,同时也会影响线程池中正在进行的任务
针对tomcat的解决办法,建项目nykj-cloud-shutdown
boot版本1.5.8、主类TomcatGracefulShutdown,在容器关闭时通过waitTime,设置容器关闭等待时间
package com.nykj.cloud.shutdown;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.catalina.connector.Connector;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Auther:
* @description:
* @Date: 18:10 2018/3/6
*/
@Slf4j
@Data
public class TomcatGracefulShutdown implements TomcatConnectorCustomizer,
ApplicationListener<ContextClosedEvent> {
private Integer waitTime;
private volatile Connector connector;
public TomcatGracefulShutdown(Integer waitTime) {
this.waitTime = waitTime;
}
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(final ContextClosedEvent event) {
LocalDateTime startShutdown = LocalDateTime.now();
LocalDateTime stopShutdown = LocalDateTime.now();
try {
log.info("We are now in down mode, please wait " + waitTime + " second(s)...");
if (connector == null) {
log.info("We are running unit test ... ");
Thread.sleep(waitTime * 1000);
return;
}
connector.pause();
final Executor executor = connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
log.info("executor is ThreadPoolExecutor");
final ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
log.warn("Tomcat thread pool did not shut down gracefully within " + waitTime + " second(s). Proceeding with force shutdown");
} else {
log.debug("Tomcat thread pool is empty, we stop now");
}
}
stopShutdown = LocalDateTime.now();
} catch (final InterruptedException ex) {
log.error("The await termination has been interrupted : " + ex.getMessage());
Thread.currentThread().interrupt();
} finally {
final long seconds = Duration.between(startShutdown, stopShutdown).getSeconds();
log.info("Shutdown performed in " + seconds + " second(s)");
}
}
}
将主类初始化到spring 容器,默认waittime是30秒
package com.nykj.cloud.shutdown;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Auther:
* @description:
* @Date: 11:47 2018/3/12
*/
@Configuration
@Slf4j
public class TomcatGracefulShutdownAutoConfiguration {
@Value("${nykj.tomcat.shutdown.waitTime:30}")
private Integer waitTime;
@Bean
public TomcatGracefulShutdown tomcatGracefulShutdown() {
TomcatGracefulShutdown tomcatGracefulShutdown = new TomcatGracefulShutdown(waitTime);
log.info("------------register tomcat graceful shutdown ... " + waitTime);
return tomcatGracefulShutdown;
}
}
设置spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.nykj.cloud.shutdown.TomcatGracefulShutdownAutoConfiguration
在微服务pom中引入,在服务中根据线程池执行时间设置关闭容器的等待时间 nykj.tomcat.shutdown.waitTime: 100
<dependency>
<groupId>com.nykj.cloudutils</groupId>
<artifactId>nykj-cloud-shutdown</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
使用/shutdown调用,先从注册中心下线,指定时间后关闭
三、其他版本
可以让服务先下线,此时服务还是存活的,可以从容处理完线程内任务。
1、DiscoveryManager.getInstance().shutdownComponent() 此方法已过时。
2、也可以在/health借口上做文章、eureka通过健康检查接口来判断服务是否健康,可以追加检查项,让服务处于down的状态。
如果有其他更优雅的方式欢迎沟通评论。
可以考虑把下线和关闭应用作为两个功能,集成到后台管理平台,或者分布式配置中心,或者容器管理工具