kill -9 關閉程式

kill -9 pid ???

kill可將指定的資訊送至程式。預設的資訊為SIGTERM(15),可將指定程式終止。若仍無法終止該程式,可使用SIGKILL(9)資訊嘗試強制刪除程式。程式或工作的編號可利用ps指令或jobs指令檢視(這段話來自菜鳥教程)。

講的這個複雜,簡單點來說就是用來殺死linux中的程序,啥?你問我啥是程序?請自行百度。

我相信很多人都用過kill -9 pid 這個命令,徹底殺死程序的意思,一般情況我們使用它沒有上面問題,但是在我們專案中使用它就有可能存在致命的問題。

kill -9 pid 帶來的問題

由於kill -9 屬於暴力刪除,所以會給程式帶來比較嚴重的後果,那究竟會帶來什麼後果呢?

舉個例子:轉賬功能,再給兩個賬戶進行加錢扣錢的時候突然斷電了?這個時候會發生什麼事情?對於InnoDB儲存引擎來說,沒有什麼損失,因為它支援事務,但是對於MyISAM引擎來說那簡直就是災難,為什麼?假如給A賬戶扣了錢,現在需要將B賬戶加錢,這個時候停電了,就會造成,A的錢被扣了,但是B沒有拿到這筆錢,這在生產環境是絕對不允許的,kill -9 相當於突然斷電的效果。

當然了,像轉賬這種,肯定不是使用MyISAM引擎,但是如今分散式火了起來,跨服務轉賬已經是很平常的事情,這種時候如果使用kill -9 去停止服務,那就不是你的事務能保證資料的準確性了,這個時候你可能會想到分散式事務,這個世界上沒有絕對的安全系統或者架構,分散式事務也是一樣,他也會存在問題,機率很小,如果一旦發生,損失有可能是無法彌補的,所以一定不能使用kill -9 去停止服務,因為你不知道他會造成什麼後果。

在MyISAM引擎中表現得更明顯,比如使用者的資訊由兩張表維護,管理員修改使用者資訊的時候需要修改兩張表,但由於你的kill -9 暴力結束專案,導致只修改成功了一張表,這也會導致資料的不一致性,這是小事,因為大不了再修改一次,但是金錢、合同這些重要的資訊如果由於你的暴力刪除導致錯亂,我覺得可能比刪庫跑路還嚴重,至少刪庫還能恢復,你這個都不知道錯在哪裡。

那我們應該怎麼結束專案呢?

其實java給我們提供了結束專案的功能,比如:tomcat可以使用shutdown。bat/shutdown。sh進行優雅結束。

什麼叫優雅結束?

“第一步:停止接收請求和內部執行緒。第二步:判斷是否有執行緒正在執行。第三步:等待正在執行的執行緒執行完畢。第四步:停止容器。”

以上四步才是正常的結束流程,那springboot怎麼正常結束服務呢?下面我介紹幾種正常結束服務的方案,請拿好小本本做好筆記。

優雅結束服務

kill -15 pid

這種方式也會比較優雅的結束程序(專案),使用他的時候需要慎重,為什麼呢?我們來看個例子

我寫了一個普通的controller方法做測試

@GetMapping(value = “/test”)    public String test(){        log。info(“test ——- start”);        try {            Thread。sleep(100000);        } catch (InterruptedException e) {            e。printStackTrace();        }        log。info(“test ——- end”);        return “test”;    }1234567891011

程式碼很簡單,列印:test — start之後讓讓程式休眠100秒,然後再列印:test — end,線上程休眠中我們使用kill -15 pid來結束這個程序,你們猜 test — end會被列印嗎?

application.yml

server:  port: 998812

啟動專案

sudo mvn spring-boot:run1

這是maven啟動springboot專案的方式

kill -9 關閉程式

看到這個就代表專案啟動成了

找到專案的程序id

sudo ps -ef |grep shutdown1

kill -9 關閉程式

這個就是專案的程序號,接下來我們先測試test介面,讓執行緒進入休眠狀態,然後再使用kill -15 14086停止專案

sudo curl 127。0。0。1:9988/test1

回到專案日誌

kill -9 關閉程式

我們發現請求已經到達服務,並且執行緒已經成功進入休眠,現在我們kill -15 14086結束程序

sudo kill -15 140861

回到日誌

2020-04-24 10:53:14。939  INFO 14086 ——- [nio-9988-exec-1] com。ymy。controller。TestController        : test ——- start2020-04-24 10:54:02。450  INFO 14086 ——- [extShutdownHook] o。s。s。concurrent。ThreadPoolTaskExecutor  : Shutting down ExecutorService ‘applicationTaskExecutor’java。lang。InterruptedException: sleep interrupted at java。lang。Thread。sleep(Native Method) at com。ymy。controller。TestController。test(TestController。java:26) at sun。reflect。NativeMethodAccessorImpl。invoke0(Native Method) at sun。reflect。NativeMethodAccessorImpl。invoke(NativeMethodAccessorImpl。java:62) at sun。reflect。DelegatingMethodAccessorImpl。invoke(DelegatingMethodAccessorImpl。java:43) at java。lang。reflect。Method。invoke(Method。java:498) at org。springframework。web。method。support。InvocableHandlerMethod。doInvoke(InvocableHandlerMethod。java:190) at org。springframework。web。method。support。InvocableHandlerMethod。invokeForRequest(InvocableHandlerMethod。java:138) at org。springframework。web。servlet。mvc。method。annotation。ServletInvocableHandlerMethod。invokeAndHandle(ServletInvocableHandlerMethod。java:105) at org。springframework。web。servlet。mvc。method。annotation。RequestMappingHandlerAdapter。invokeHandlerMethod(RequestMappingHandlerAdapter。java:879) at org。springframework。web。servlet。mvc。method。annotation。RequestMappingHandlerAdapter。handleInternal(RequestMappingHandlerAdapter。java:793) at org。springframework。web。servlet。mvc。method。AbstractHandlerMethodAdapter。handle(AbstractHandlerMethodAdapter。java:87) at org。springframework。web。servlet。DispatcherServlet。doDispatch(DispatcherServlet。java:1040) at org。springframework。web。servlet。DispatcherServlet。doService(DispatcherServlet。java:943) at org。springframework。web。servlet。FrameworkServlet。processRequest(FrameworkServlet。java:1006) at org。springframework。web。servlet。FrameworkServlet。doGet(FrameworkServlet。java:898) at javax。servlet。http。HttpServlet。service(HttpServlet。java:634) at org。springframework。web。servlet。FrameworkServlet。service(FrameworkServlet。java:883) at javax。servlet。http。HttpServlet。service(HttpServlet。java:741) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:231) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。apache。tomcat。websocket。server。WsFilter。doFilter(WsFilter。java:53) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。springframework。web。filter。RequestContextFilter。doFilterInternal(RequestContextFilter。java:100) at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。springframework。web。filter。FormContentFilter。doFilterInternal(FormContentFilter。java:93) at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。springframework。boot。actuate。metrics。web。servlet。WebMvcMetricsFilter。doFilterInternal(WebMvcMetricsFilter。java:109) at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。springframework。web。filter。CharacterEncodingFilter。doFilterInternal(CharacterEncodingFilter。java:201) at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) at org。apache。catalina。core。StandardWrapperValve。invoke(StandardWrapperValve。java:202) at org。apache。catalina。core。StandardContextValve。invoke(StandardContextValve。java:96) at org。apache。catalina。authenticator。AuthenticatorBase。invoke(AuthenticatorBase。java:541) at org。apache。catalina。core。StandardHostValve。invoke(StandardHostValve。java:139) at org。apache。catalina。valves。ErrorReportValve。invoke(ErrorReportValve。java:92) at org。apache。catalina。core。StandardEngineValve。invoke(StandardEngineValve。java:74) at org。apache。catalina。connector。CoyoteAdapter。service(CoyoteAdapter。java:343) at org。apache。coyote。http11。Http11Processor。service(Http11Processor。java:373) at org。apache。coyote。AbstractProcessorLight。process(AbstractProcessorLight。java:65) at org。apache。coyote。AbstractProtocol$ConnectionHandler。process(AbstractProtocol。java:868) at org。apache。tomcat。util。net。NioEndpoint$SocketProcessor。doRun(NioEndpoint。java:1594) at org。apache。tomcat。util。net。SocketProcessorBase。run(SocketProcessorBase。java:49) at java。util。concurrent。ThreadPoolExecutor。runWorker(ThreadPoolExecutor。java:1149) at java。util。concurrent。ThreadPoolExecutor$Worker。run(ThreadPoolExecutor。java:624) at org。apache。tomcat。util。threads。TaskThread$WrappingRunnable。run(TaskThread。java:61) at java。lang。Thread。run(Thread。java:748)2020-04-24 10:54:04。574  INFO 14086 ——- [nio-9988-exec-1] com。ymy。controller。TestController        : test ——- end2020-04-24 10:54:04。610 ERROR 14086 ——- [nio-9988-exec-1] o。s。web。servlet。HandlerExecutionChain    : HandlerInterceptor。afterCompletion threw exceptionjava。lang。NullPointerException: null at org。springframework。boot。actuate。metrics。web。servlet。LongTaskTimingHandlerInterceptor。stopLongTaskTimers(LongTaskTimingHandlerInterceptor。java:123) ~[spring-boot-actuator-2。2。6。RELEASE。jar:2。2。6。RELEASE] at org。springframework。boot。actuate。metrics。web。servlet。LongTaskTimingHandlerInterceptor。afterCompletion(LongTaskTimingHandlerInterceptor。java:79) ~[spring-boot-actuator-2。2。6。RELEASE。jar:2。2。6。RELEASE] at org。springframework。web。servlet。HandlerExecutionChain。triggerAfterCompletion(HandlerExecutionChain。java:179) ~[spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。servlet。DispatcherServlet。triggerAfterCompletion(DispatcherServlet。java:1427) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。servlet。DispatcherServlet。doDispatch(DispatcherServlet。java:1060) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。servlet。DispatcherServlet。doService(DispatcherServlet。java:943) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。servlet。FrameworkServlet。processRequest(FrameworkServlet。java:1006) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。servlet。FrameworkServlet。doGet(FrameworkServlet。java:898) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at javax。servlet。http。HttpServlet。service(HttpServlet。java:634) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。springframework。web。servlet。FrameworkServlet。service(FrameworkServlet。java:883) [spring-webmvc-5。2。5。RELEASE。jar:5。2。5。RELEASE] at javax。servlet。http。HttpServlet。service(HttpServlet。java:741) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:231) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。tomcat。websocket。server。WsFilter。doFilter(WsFilter。java:53) [tomcat-embed-websocket-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。springframework。web。filter。RequestContextFilter。doFilterInternal(RequestContextFilter。java:100) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。springframework。web。filter。FormContentFilter。doFilterInternal(FormContentFilter。java:93) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。springframework。boot。actuate。metrics。web。servlet。WebMvcMetricsFilter。doFilterInternal(WebMvcMetricsFilter。java:109) [spring-boot-actuator-2。2。6。RELEASE。jar:2。2。6。RELEASE] at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。springframework。web。filter。CharacterEncodingFilter。doFilterInternal(CharacterEncodingFilter。java:201) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。springframework。web。filter。OncePerRequestFilter。doFilter(OncePerRequestFilter。java:119) [spring-web-5。2。5。RELEASE。jar:5。2。5。RELEASE] at org。apache。catalina。core。ApplicationFilterChain。internalDoFilter(ApplicationFilterChain。java:193) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。ApplicationFilterChain。doFilter(ApplicationFilterChain。java:166) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。StandardWrapperValve。invoke(StandardWrapperValve。java:202) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。StandardContextValve。invoke(StandardContextValve。java:96) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。authenticator。AuthenticatorBase。invoke(AuthenticatorBase。java:541) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。StandardHostValve。invoke(StandardHostValve。java:139) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。valves。ErrorReportValve。invoke(ErrorReportValve。java:92) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。core。StandardEngineValve。invoke(StandardEngineValve。java:74) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。catalina。connector。CoyoteAdapter。service(CoyoteAdapter。java:343) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。coyote。http11。Http11Processor。service(Http11Processor。java:373) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。coyote。AbstractProcessorLight。process(AbstractProcessorLight。java:65) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。coyote。AbstractProtocol$ConnectionHandler。process(AbstractProtocol。java:868) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。tomcat。util。net。NioEndpoint$SocketProcessor。doRun(NioEndpoint。java:1594) [tomcat-embed-core-9。0。33。jar:9。0。33] at org。apache。tomcat。util。net。SocketProcessorBase。run(SocketProcessorBase。java:49) [tomcat-embed-core-9。0。33。jar:9。0。33] at java。util。concurrent。ThreadPoolExecutor。runWorker(ThreadPoolExecutor。java:1149) [na:1。8。0_242] at java。util。concurrent。ThreadPoolExecutor$Worker。run(ThreadPoolExecutor。java:624) [na:1。8。0_242] at org。apache。tomcat。util。threads。TaskThread$WrappingRunnable。run(TaskThread。java:61) [tomcat-embed-core-9。0。33。jar:9。0。33] at java。lang。Thread。run(Thread。java:748) [na:1。8。0_242]123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112

居然報錯了,但是test — end是打印出來了,為什麼會報錯呢?這就和sleep這個方法有關了,線上程休眠期間,當呼叫執行緒的interrupt方法的時候會導致sleep丟擲異常,這裡很明顯就是kill -15 這個命令會讓程式馬上呼叫執行緒的interrupt方法,目的是為了讓執行緒停止,雖然讓執行緒停止,但執行緒什麼時候停止還是執行緒自己說的算,這就是為什麼我們還能看到:test — end的原因。更多的 Spring Boot 影片教程可以關注微信訂閱號碼將筆記回覆 Spring Boot 獲取。

ConfigurableApplicationContext colse

我們先看怎麼實現

package com。ymy。controller;import lombok。extern。slf4j。Slf4j;import org。springframework。beans。BeansException;import org。springframework。context。ApplicationContext;import org。springframework。context。ApplicationContextAware;import org。springframework。context。ConfigurableApplicationContext;import org。springframework。web。bind。annotation。GetMapping;import org。springframework。web。bind。annotation。PostMapping;import org。springframework。web。bind。annotation。RestController;@RestController@Slf4jpublic class TestController  implements ApplicationContextAware {    private  ApplicationContext  context;    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this。context = applicationContext;    }    @GetMapping(value = “/test”)    public String test(){        log。info(“test ——- start”);        try {            Thread。sleep(100000);        } catch (InterruptedException e) {            e。printStackTrace();        }        log。info(“test ——- end”);        return “test”;    }    /**     * 停機     */    @PostMapping(value = “shutdown”)    public void shutdown(){        ConfigurableApplicationContext cyx = (ConfigurableApplicationContext) context;        cyx。close();    }}1234567891011121314151617181920212223242526272829303132333435363738394041424344

重點在:cyx。close();,為什麼他能停止springboot專案呢?請看原始碼

public void close() {        synchronized(this。startupShutdownMonitor) {            this。doClose();            if (this。shutdownHook != null) {                try {                    Runtime。getRuntime()。removeShutdownHook(this。shutdownHook);                } catch (IllegalStateException var4) {                }            }        }    }123456789101112

程式在啟動的時候向jvm註冊了一個關閉鉤子,我們在執行colse方法的時候會刪除這個關閉鉤子,jvm就會知道這是需要停止服務。

我們看測試結果

kill -9 關閉程式

kill -9 關閉程式

很明顯,他也觸發了執行緒的interrupt方法導致執行緒報錯,原理和kill -15差不多。

actuator

這種方式是透過引入依賴的方式停止服務,actuator提供了很多介面,比如健康檢查,基本資訊等等,我們也可以使用他來優雅的停機。

引入依賴

            org。springframework。boot            spring-boot-starter-actuator        12345

application.yml

server:  port: 9988management:  endpoints:    web:      exposure:        include: shutdown  endpoint:    shutdown:      enabled: true  server:    port: 888812345678910111213

我這裡對actuator的介面重新給定了一個介面,這樣可提高安全性,下面我們來測試一下

@RequestMapping(value = “/test”,method = RequestMethod。GET)    public String test(){        System。out。println(“test ——- start”);        try {            Thread。sleep(10000);        } catch (InterruptedException e) {            e。printStackTrace();        }        System。out。println(“test ——- end”);        return “hello”;    }1234567891011

在請求test途中停止服務

kill -9 關閉程式

我們發現傳送停止服務請求之後還給我們返回了提示資訊,很人性化,我們看看控制檯

kill -9 關閉程式

kill -9 關閉程式

test — end被執行了,不過在停止執行緒池的時候還是呼叫了執行緒的interrupt方法,導致sleep報錯,這三種方式都可以比較優雅的停止springboot服務,如果我專案中存線上程休眠,我希望10秒以後再停止服務可以嗎?肯定是可以的,我們只需要稍微做點修改就可以了。

1.新增停止springboot服務類:ElegantShutdownConfig.java

package com。ymy。config;import org。apache。catalina。connector。Connector;import org。springframework。boot。web。embedded。tomcat。TomcatConnectorCustomizer;import org。springframework。context。ApplicationListener;import org。springframework。context。event。ContextClosedEvent;import java。util。concurrent。Executor;import java。util。concurrent。ThreadPoolExecutor;import java。util。concurrent。TimeUnit;public class ElegantShutdownConfig implements TomcatConnectorCustomizer, ApplicationListener {    private volatile Connector connector;    private final int waitTime = 10;    @Override    public void customize(Connector connector) {        this。connector = connector;    }    @Override    public void onApplicationEvent(ContextClosedEvent event) {        connector。pause();        Executor executor = connector。getProtocolHandler()。getExecutor();        if (executor instanceof ThreadPoolExecutor) {            try {                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;                threadPoolExecutor。shutdown();                if (!threadPoolExecutor。awaitTermination(waitTime, TimeUnit。SECONDS)) {                    System。out。println(“請嘗試暴力關閉”);                }            } catch (InterruptedException ex) {                System。out。println(“異常了”);                Thread。currentThread()。interrupt();            }        }    }}1234567891011121314151617181920212223242526272829303132333435363738394041

2.在啟動類中加入bean

package com。ymy;import com。ymy。config。ElegantShutdownConfig;import lombok。extern。slf4j。Slf4j;import org。apache。catalina。connector。Connector;import org。springframework。boot。SpringApplication;import org。springframework。boot。autoconfigure。SpringBootApplication;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。ConfigurableApplicationContext;import org。springframework。context。annotation。Bean;import org。springframework。context。event。ContextClosedEvent;import java。util。concurrent。Executor;import java。util。concurrent。ThreadPoolExecutor;import java。util。concurrent。TimeUnit;@SpringBootApplicationpublic class ShutdownServerApplication {    public static void main(String[] args) {        ConfigurableApplicationContext run = SpringApplication。run(ShutdownServerApplication。class, args);        run。registerShutdownHook();    }    @Bean    public ElegantShutdownConfig elegantShutdownConfig() {        return new ElegantShutdownConfig();    }    @Bean    public ServletWebServerFactory servletContainer() {        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();        tomcat。addConnectorCustomizers(elegantShutdownConfig());        return tomcat;    }}1234567891011121314151617181920212223242526272829303132333435363738394041

這樣我們就配置好了,我們再來測試一遍,test的介面還是休眠10秒

kill -9 關閉程式

我們發現這次沒有報錯了,他是等待了一段時間之後再結束的執行緒池,這個時間就是我們在ElegantShutdownConfig類中配置的waitTime。

那可能你會有疑問了,jvm沒有立即停止,那這個時候在有請求會發生什麼呢?如果關閉的時候有新的請求,服務將不再接收此請求。

資料備份操作

如果我想在服務停止的時候做點備份操作啥的,應該怎麼做呢?其實很簡單在你要執行的方法上新增一個註解即可:@PreDestroy

“Destroy:消滅、毀滅 pre:字首縮寫”

所以合在一起的意思就是在容器停止之前執行一次,你可以在這裡面做備份操作,也可以做記錄停機時間等。

新增服務停止備份工具類:DataBackupConfig。java

package com。ymy。config;import org。springframework。context。annotation。Configuration;import javax。annotation。PreDestroy;@Configurationpublic class DataBackupConfig {    @PreDestroy    public  void backData(){        System。out。println(“正在備份資料……。。。。。”);    }}123456789101112131415

我們再來測試然後列印控制檯日誌:

kill -9 關閉程式