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
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 }
浙公网安备 33010602011771号