前言
往往"停止服務"的代名詞就是暴力,不計後果的,因為在強制停止的時候,不會管裡面是否還有正在運作的線程。
碰巧最近由于在搞AWS的auto scalinng,不知道的朋友,可以把它了解為AWS可以自動的擴充或者是收縮我們的伺服器,使得可以減少經費,想更深入了解的可以自行google。
這個出發點好是好,但是我也在實際使用的時候,發現了點問題:如果docker被stop了,裡面可能存活的就被強制停止了,這個時候我麼應該怎麼辦呢?
正文
根據
- docker stop指令實際上執行的是kill pid 指令,如果不跟随停止信号的話,預設情況下使用的是SIGNTEMR
- 并且如果docker中的主程序被停止,那麼docker自然會停止。
是以推斷問題的關鍵在于,我們需要去操控spring boot 需要優雅的stop,也就是我們今天的主角。
說了這麼多廢話該提起,下面進入正題,網上其實有很多這方面的教程例如說下面這個就寫的很好:
https://www.cnblogs.com/harrychinese/p/SpringBoot-graceful-shutdown.html但是網上的文檔幾乎都是把注入bean放在啟動類中的,而我給它放在了@configuration 的類裡,下面呢看下主要代碼:
首先是最主要的監聽容器關閉,并且進行處理的代碼:
package com.demo.timeout.tomcat;
import org.apache.catalina.connector.Connector;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Component
public class GracefulShutdown implements TomcatConnectorCustomizer, ApplicationListener<ContextClosedEvent> {
private volatile Connector connector;
private int waitTime;
@Value("${STOP_WAIT_TIMEOUT}")
public void setWaitTime(int waitTime) {
this.waitTime = waitTime;
}
@Override
public void customize(Connector connector) {
this.connector = connector;
}
@Override
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
this.connector.pause();
Executor executor = this.connector.getProtocolHandler().getExecutor();
try {
if (executor instanceof ThreadPoolExecutor) {
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;
threadPoolExecutor.shutdown();
if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {
System.out.println("Tomcat 程序在" + waitTime + " 秒内無法結束,嘗試強制結束");
}
}
} catch (Exception e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
}
然後是将其注入的代碼:
package com.demo.timeout.config;
import com.demo.timeout.tomcat.GracefulShutdown;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class GracefulShutDownConfig {
@Autowired
private GracefulShutdown gracefulShutdown;
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
tomcatServletWebServerFactory.addConnectorCustomizers(gracefulShutdown);
return tomcatServletWebServerFactory;
}
}
最後寫了一個API的測試代碼
package com.demo.timeout.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/30s-process")
public String longProcessAPI(){
System.out.println("enter this long process function");
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "30s線程啟動";
}
}
如果有需要可以看一下我上傳的代碼:
https://github.com/luckypoison/SpringBoot-Shutdown-Graceful另外說一下如果用的不是spring boot内嵌的tomcat,那麼我們隻能通過改tomcat的配置。
具體的改法是我們應該修改conf檔案下的context.xml檔案,加上一個“unloadDelay”屬性,這個屬性的值為逾時時間的值,如果在這個時間之内運作完了,則tomcat關閉,否則tomcat将強制關閉,代碼案例如下:
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context unloadDelay="60000">
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
</Context>