1. <em id="vzzs9"></em>
      <tbody id="vzzs9"></tbody>

    2. <span id="vzzs9"></span>
      <progress id="vzzs9"></progress>
      首頁 運維干貨別再用kill -9關閉程序了......

      別再用kill -9關閉程序了......

      運維派隸屬馬哥教育旗下專業運維社區,是國內成立最早的IT運維技術社區,歡迎關注公眾號:yunweipai
      領取學習更多免費Linux云計算、Python、Docker、K8s教程關注公眾號:馬哥linux運維

      別再用kill -9關閉程序了……

      別再用kill -9關閉程序了......插圖

      kill 可將指定的信息送至程序。預設的信息為 SIGTERM(15),可將指定程序終止。若仍無法終止該程序,可使用 SIGKILL(9) 信息嘗試強制刪除程序。程序或工作的編號可利用 ps 指令或 jobs 指令查看。

      kill -9 pid ???

      講的這個復雜,簡單點來說就是用來殺死 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";
          }
      

      代碼很簡單,打?。簍est — start 之后讓讓程序休眠 100 秒,然后再打?。簍est — end。

      在線程休眠中我們使用 kill -15 pid 來結束這個進程,你們猜 test — end 會被打印嗎?

      application.yml:

      server:??port:?9988
      

      啟動項目:

      sudo?mvn?spring-boot:run
      

      這是 maven 啟動 SpringBoot 項目的方式:

      別再用kill -9關閉程序了......插圖1

      看到這個就代表項目啟動成了,找到項目的進程 id:

      sudo?ps?-ef?|grep?shutdown
      

      別再用kill -9關閉程序了......插圖2

      這個就是項目的進程號,接下來我們先測試 test 接口,讓線程進入休眠狀態,然后再使用 kill -15 14086 停止項目:

      sudo?curl?127.0.0.1:9988/test
      

      回到項目日志:

      別再用kill -9關閉程序了......插圖3

      我們發現請求已經到達服務,并且線程已經成功進入休眠,現在我們 kill -15 14086 結束進程:

      sudo?kill?-15?14086
      

      回到日志:

      2020-04-24 10:53:14.939  INFO 14086 --- [nio-9988-exec-1] com.ymy.controller.TestController        : test --- start
      2020-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 --- end
      2020-04-24 10:54:04.610 ERROR 14086 --- [nio-9988-exec-1] o.s.web.servlet.HandlerExecutionChain    : HandlerInterceptor.afterCompletion threw exception
      
      java.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]
      

      居然報錯了,但是 test — end 是打印出來了,為什么會報錯呢?

      別再用kill -9關閉程序了......插圖4

      這就和 sleep 這個方法有關了,在線程休眠期間,當調用線程的 interrupt 方法的時候會導致 sleep 拋出異常。

      這里很明顯就是 kill -15 這個命令會讓程序馬上調用線程的 interrupt 方法,目的是為了讓線程停止。

      雖然讓線程停止,但線程什么時候停止還是線程自己說的算,這就是為什么我們還能看到:test — end 的原因。

      ②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
      @Slf4j
      public 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();
          }
      }
      

      重點在:cyx.close(); 為什么他能停止 SpringBoot 項目呢?請看源碼:

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

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

      我們看測試結果:

      別再用kill -9關閉程序了......插圖5

      別再用kill -9關閉程序了......插圖6

      很明顯,他也出發了線程的 interrupt 方法導致線程報錯,原理和 kill -15 差不多。

      ③actuator

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

      引入依賴:

                  org.springframework.boot
                  spring-boot-starter-actuator
      
      

      application.yml:

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

      我這里對 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";
          }
      

      在請求 test 途中停止服務:

      別再用kill -9關閉程序了......插圖7

      我們發現發送停止服務請求之后還給我們返回了提示信息,很人性化,我們看看控制臺:

      別再用kill -9關閉程序了......插圖8

      別再用kill -9關閉程序了......插圖9

      test — end 被執行了,不過在停止線程池的時候還是調用了線程的 interrupt 方法,導致 sleep 報錯,這三種方式都可以比較優雅的停止 SpringBoot 服務。

      如果我項目中存在線程休眠,我希望 10 秒以后再停止服務可以嗎?肯定是可以的,我們只需要稍微做點修改就可以了。

      新增停止 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();
                  }
              }
      
          }
      }
      

      在啟動類中加入 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;
      
      @SpringBootApplication
      public 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;
          }
      }
      

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

      別再用kill -9關閉程序了......插圖10

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

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

      數據備份操作

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

      • Destroy:消滅、毀滅。
      • pre:前綴縮寫。

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

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

      package com.ymy.config;
      
      import org.springframework.context.annotation.Configuration;
      
      import javax.annotation.PreDestroy;
      
      @Configuration
      public class DataBackupConfig {
      
          @PreDestroy
          public  void backData(){
      
              System.out.println("正在備份數據。。。。。。。。。。。");
          }
      }
      

      我們再來測試然后打印控制臺日志:

      別再用kill -9關閉程序了......插圖11
      別再用kill -9關閉程序了......插圖12

      作者:流星007
      編輯:陶家龍
      出處:http://45dwz.com/GrTFl
      (版權歸原作者所有,侵刪)

      本文鏈接:http://www.abandonstatusquo.com/39910.html

      網友評論comments

      發表評論

      您的電子郵箱地址不會被公開。

      暫無評論

      Copyright ? 2012-2022 YUNWEIPAI.COM - 運維派 京ICP備16064699號-6
      掃二維碼
      掃二維碼
      返回頂部
      久久久久亚洲国内精品|亚洲一区二区在线观看综合无码|欧洲一区无码精品色|97伊人久久超碰|一级a爱片国产亚洲精品