SpringBoot 优雅重启
Posted SW_Sovereign
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 优雅重启相关的知识,希望对你有一定的参考价值。
由于springboot项目是打成jar包运行,所以在维护过程中需要不断更新;每次都是上传jar,执行 ps -ef|grep java 命令查找java进程,kill pid,nohup java -jar test.jar ;太麻烦了,所以就做了重启脚本;
1,在项目中添加shutdown配置类
Spring Boot 1.X
1 import java.util.concurrent.Executor; 2 import java.util.concurrent.ThreadPoolExecutor; 3 import java.util.concurrent.TimeUnit; 4 import org.apache.catalina.connector.Connector; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; 8 import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; 9 import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer; 10 import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 11 import org.springframework.context.ApplicationListener; 12 import org.springframework.context.annotation.Bean; 13 import org.springframework.context.annotation.Configuration; 14 import org.springframework.context.event.ContextClosedEvent; 15 /** 16 * Spring Boot1.X Tomcat容器优雅停机 17 * 18 */ 19 @Configuration 20 public class ShutdownConfig { 21 /** 22 * 用于接受shutdown事件 23 * @return 24 */ 25 @Bean 26 public GracefulShutdown gracefulShutdown() { 27 return new GracefulShutdown(); 28 } 29 /** 30 * 用于注入 connector 31 * @return 32 */ 33 @Bean 34 public EmbeddedServletContainerCustomizer tomcatCustomizer() { 35 return new EmbeddedServletContainerCustomizer() { 36 @Override 37 public void customize(ConfigurableEmbeddedServletContainer container) { 38 if (container instanceof TomcatEmbeddedServletContainerFactory) { 39 ((TomcatEmbeddedServletContainerFactory) container).addConnectorCustomizers(gracefulShutdown()); 40 } 41 } 42 }; 43 } 44 private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> { 45 private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class); 46 private volatile Connector connector; 47 private final int waitTime = 120; 48 @Override 49 public void customize(Connector connector) { 50 this.connector = connector; 51 } 52 @Override 53 public void onApplicationEvent(ContextClosedEvent event) { 54 this.connector.pause(); 55 Executor executor = this.connector.getProtocolHandler().getExecutor(); 56 if (executor instanceof ThreadPoolExecutor) { 57 try { 58 ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; 59 log.info("shutdown start"); 60 threadPoolExecutor.shutdown(); 61 log.info("shutdown end"); 62 if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) { 63 log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束"); 64 } 65 log.info("shutdown success"); 66 } catch (InterruptedException ex) { 67 Thread.currentThread().interrupt(); 68 } 69 } 70 } 71 } 72 }
Spring Boot 2.X
1 import java.util.concurrent.Executor; 2 import java.util.concurrent.ThreadPoolExecutor; 3 import java.util.concurrent.TimeUnit; 4 import org.apache.catalina.connector.Connector; 5 import org.slf4j.Logger; 6 import org.slf4j.LoggerFactory; 7 import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; 8 import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; 9 import org.springframework.boot.web.servlet.server.ServletWebServerFactory; 10 import org.springframework.context.ApplicationListener; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration; 13 import org.springframework.context.event.ContextClosedEvent; 14 /** 15 * Spring Boot2.X Tomcat容器优雅停机 16 * 17 */ 18 @Configuration 19 public class ShutdownConfig { 20 /** 21 * 用于接受shutdown事件 22 * @return 23 */ 24 @Bean 25 public GracefulShutdown gracefulShutdown() { 26 return new GracefulShutdown(); 27 } 28 @Bean 29 public ServletWebServerFactory servletContainer() { 30 TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); 31 tomcat.addConnectorCustomizers(gracefulShutdown()); 32 return tomcat; 33 } 34 private static class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> { 35 private static final Logger log = LoggerFactory.getLogger(GracefulShutdown.class); 36 private volatile Connector connector; 37 private final int waitTime = 120; 38 @Override 39 public void customize(Connector connector) { 40 this.connector = connector; 41 } 42 @Override 43 public void onApplicationEvent(ContextClosedEvent event) { 44 this.connector.pause(); 45 Executor executor = this.connector.getProtocolHandler().getExecutor(); 46 if (executor instanceof ThreadPoolExecutor) { 47 try { 48 ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor; 49 log.info("shutdown start"); 50 threadPoolExecutor.shutdown(); 51 log.info("shutdown end"); 52 if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) { 53 log.info("Tomcat 进程在" + waitTime + "秒内无法结束,尝试强制结束"); 54 } 55 log.info("shutdown success"); 56 } catch (InterruptedException ex) { 57 Thread.currentThread().interrupt(); 58 } 59 } 60 } 61 } 62 }
2,重启服务脚本
1 #!/bin/sh 2 JAVA_OPTS=‘-Xms128m -Xmx512m -XX:NewSize=128m -XX:MaxNewSize=512m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=128m -XX:NewRatio=2 -XX:MaxTenuringThreshold=8 -XX:+DisableExplicitGC‘ 3 RESOURCE_NAME=/home/test-0.0.1-SNAPSHOT.jar 4 LOG_NAME=/home/test.log 5 MAX_TIMEOUT=20 6 7 tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk ‘{print $2}‘` 8 9 if [ ${tpid} ]; then 10 echo ‘Stop Process...‘ 11 kill -15 $tpid 12 fi 13 14 for((i=0;i<$MAX_TIMEOUT;i++)) 15 do 16 sleep 1 17 tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk ‘{print $2}‘` 18 if [ ${tpid} ]; then 19 echo ‘App Stoping...‘ 20 else 21 break 22 fi 23 done 24 25 if [ ${tpid} ]; then 26 echo ‘Kill Process!‘ 27 kill -9 $tpid 28 else 29 echo ‘Stop Success!‘ 30 fi 31 32 tpid=`ps -ef|grep $RESOURCE_NAME|grep -v grep|grep -v kill|awk ‘{print $2}‘` 33 34 if [ ${tpid} ]; then 35 echo ‘App is running.‘ 36 else 37 echo ‘App is NOT running.‘ 38 fi 39 40 rm -f tpid 41 42 echo ‘App is Starting...‘ 43 nohup java $JAVA_OPTS -jar $RESOURCE_NAME >$LOG_NAME 2>&1 & 44 echo $! > tpid 45 echo Start Success!
3,测试
编写简单的接口,在接口中等待,然后执行脚本停止项目,如果正常的话会输出服务停止中,等到你的接口执行完成,进程才会消失掉,但是如果超过了你配置的等待时间就会强行退出。
1 @RequestMapping("/test") 2 public String test() throws InterruptedException 3 { 4 5 log.info("接口开始执行..."); 6 log.info("接口执行中..."); 7 final CountDownLatch latch = new CountDownLatch(12); 8 ListeningExecutorService pool = null; 9 10 pool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1)); 11 12 for (int i = 0; i < 12; i++) { 13 ListenableFuture<String> listenableFuture = pool.submit(() -> { 14 15 Thread.sleep(1000); 16 log.info("Thread Name:{}", Thread.currentThread().getName()); 17 return "OK"; 18 }); 19 20 Futures.addCallback(listenableFuture, new FutureCallback<String>() 21 { 22 public void onSuccess(String orderList) 23 { 24 latch.countDown(); 25 log.info("{}--{}", orderList, Thread.currentThread().getName()); 26 } 27 28 public void onFailure(Throwable throwable) 29 { 30 latch.countDown(); 31 System.out.println(throwable.getMessage()); 32 } 33 }); 34 } 35 36 latch.await(); 37 pool.shutdown(); 38 39 log.info("接口执行完成..."); 40 log.info("系统正常退出..."); 41 return "OK"; 42 }
以上是关于SpringBoot 优雅重启的主要内容,如果未能解决你的问题,请参考以下文章
优雅写代码系统springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码