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 优雅重启的主要内容,如果未能解决你的问题,请参考以下文章

spring boot 2.0 实现优雅停机

Spring Boot 1.X和2.X优雅重启实战

用代码优雅的终止springboot服务

优雅写代码系统springboot+mybatis+pagehelper+mybatisplus+druid教你如何优雅写代码

如何优雅地重启芹菜工人?

SpringBoot 优雅停机