Spring Boot 优雅重启
转载自:https://www.cnblogs.com/hpxiaokang/p/9300933.html
1,在项目中添加shutdown配置类
Spring Boot 1.x
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
/**
* Spring Boot1.X Tomcat容器优雅停机
*
*/
@Configuration
public class ShutdownConfig {
/**
* 用于接受shutdown事件
* @return
*/
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
/**
* 用于注入 connector
* @return
*/
@Bean
public EmbeddedServletContainerCustomizer tomcatCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(gracefulShutdown());
}
}
};
}
private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private volatile Connector connector;
private final int waitTime = 120;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
log.info("shutdown start");
threadPoolExecutor.shutdown();
log.info("shutdown end");
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
}
log.info("shutdown success");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
}
Spring Boot 2.x
package com.example.demo.config;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.apache.catalina.connector.Connector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
/**
* Spring Boot2.X Tomcat容器优雅停机
*
*/
@Configuration
public class ShutdownConfig {
/**
* 用于接受shutdown事件
* @return
*/
@Bean
public GracefulShutdown gracefulShutdown() {
return new GracefulShutdown();
}
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
tomcat.addConnectorCustomizers(gracefulShutdown());
return tomcat;
}
private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class);
private volatile Connector connector;
private final int waitTime = 120;
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent event) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
if (executor instanceof ThreadPoolExecutor) {
try {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
log.info("shutdown start");
threadPoolExecutor.shutdown();
log.info("shutdown end");
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束");
}
log.info("shutdown success");
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}
}
2,重启服务脚本
#!/bin/sh
JAVA_OPTS='-Xms128m -Xmx512m -XX:NewSize=128m -XX:MaxNewSize=512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m -XX:NewRatio=2 -XX:MaxTenuringThreshold=8 -XX:+DisableExplicitGC'
RESOURCE_NAME=/root/test/sb/demo.jar
NEW_NAME=/root/tmp/sb/demo.jar
LOG_NAME=/home/test.log
MAX_TIMEOUT=20
tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'Stop Process...'
kill -15 $tpid
fi
for((i=0;i<$MAX_TIMEOUT;i++))
do
sleep 1
tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'App Stoping...'
else
break
fi
done
if [ ${tpid} ]; then
echo 'Kill Process!'
kill -9 $tpid
else
echo 'Stop Success!'
fi
tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk '{print $2}'`
if [ ${tpid} ]; then
echo 'App is running.'
else
echo 'App is NOT running.'
fi
rm -f tpid
echo 'App is Starting...'
nohup java $JAVA_OPTS -jar $NEW_NAME >$LOG_NAME 2>&1 &
echo $! > tpid
echo Start Success!
3,测试
编写简单的接口,在接口中等待,然后执行脚本停止项目,如果正常的话会输出服务停止中,等到你的接口执行完成,进程才会消失掉,但是如果超过了你配置的等待时间就会强行退出。
@RequestMapping("/test")
public String test() throws InterruptedException
{
log.info("接口开始执行...");
log.info("接口执行中...");
final CountDownLatch latch = new CountDownLatch(12);
ListeningExecutorService pool = null;
pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));
for (int i = 0; i < 12; i++) {
ListenableFuture<String> listenableFuture = pool.submit(() -> {
Thread.sleep(1000);
log.info("Thread Name:{}", Thread.currentThread().getName());
return "OK";
});
Futures.addCallback(listenableFuture, new FutureCallback<String>()
{
public void onSuccess(String orderList)
{
latch.countDown();
log.info("{}--{}", orderList, Thread.currentThread().getName());
}
public void onFailure(Throwable throwable)
{
latch.countDown();
System.out.println(throwable.getMessage());
}
});
}
latch.await();
pool.shutdown();
log.info("接口执行完成...");
log.info("系统正常退出...");
return "OK";
}