最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring?Boot?優(yōu)雅停機原理詳解

 更新時間:2023年02月16日 09:23:22   作者:政采云技術團隊  
這篇文章主要為大家介紹了Spring?Boot?優(yōu)雅停機原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

正文

SpringBoot 從2.3.0.RELEASE 開始支持 web 服務器的優(yōu)雅停機

看看官方文檔是怎么介紹這一新特性的

“ Graceful shutdown is supported with all four embedded web servers (Jetty, Reactor Netty, Tomcat, and Undertow) and with both reactive and Servlet-based web applications. It occurs as part of closing the application context and is performed in the earliest phase of stopping SmartLifecycle beans. This stop processing uses a timeout which provides a grace period during which existing requests will be allowed to complete but no new requests will be permitted. The exact way in which new requests are not permitted varies depending on the web server that is being used. Jetty, Reactor Netty, and Tomcat will stop accepting requests at the network layer. Undertow will accept requests but respond immediately with a service unavailable (503) response."

四種內嵌 web 服務器(Jetty、Reactor Netty、Tomcat 和 Undertow)以及 reactive 和基于 servlet 的 web 應用程序都支持優(yōu)雅停機,它作為關閉應用程序上下文的一部分發(fā)生,并且是SmartLifecyclebean里最早進行關閉的。此停止處理會有個超時機制,該超時提供了一個寬限期,在此期間允許完成現(xiàn)有請求,但不允許新請求。具體實現(xiàn)取決于所使用的web服務器。Jetty、Reactor Netty 和 Tomcat 將停止接受網(wǎng)絡層的請求。Undertow 將接受請求,但立即響應服務不可用(503)。

如何開啟優(yōu)雅停機

server:
  # 設置關閉方式為優(yōu)雅關閉
  shutdown: graceful
spring:
  lifecycle:
    # 優(yōu)雅關閉超時時間, 默認30s
    timeout-per-shutdown-phase: 30s

優(yōu)雅停機原理

shutdown hook

在 Java 程序中可以通過添加鉤子,在程序退出時會執(zhí)行鉤子方法,從而實現(xiàn)關閉資源、平滑退出等功能。

public static void main(String[] args) {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("執(zhí)行 ShutdownHook ...");
            }
        }));
    }

覆蓋以下場景:

  • 代碼主動關閉:如System.exit()
  • 捕獲kill信號: kill -1(HUP), kill - 2(INT), kill -15(TERM)
  • 用戶線程結束: 會在最后一個非守護線程結束時被 JNI 的DestroyJavaVM方法調用

說明: kill -9 會直接殺死進程不會觸發(fā) shutdownhook 方法執(zhí)行,shutdownhook 回調方法會啟動新線程,注冊多個鉤子會并發(fā)執(zhí)行。

SpringBoot注冊 Shutdown Hook

SpringBoot 在啟動過程中,則會默認注冊一個 Shutdown Hook,在應用被關閉的時候,會觸發(fā)鉤子調用 doClose()方法,去關閉容器。(也可以通過 actuate 來優(yōu)雅關閉應用,不在本文討論范圍)

org.springframework.boot.SpringApplication#refreshContext

private void refreshContext(ConfigurableApplicationContext context) {
   // 默認為true
   if (this.registerShutdownHook) {
      try {
         context.registerShutdownHook();
      }
      catch (AccessControlException ex) {
         // Not allowed in some environments.
      }
   }
   refresh((ApplicationContext) context);
}

org.springframework.context.support.AbstractApplicationContext

@Override
public void registerShutdownHook() {
   if (this.shutdownHook == null) {
      // No shutdown hook registered yet.
      this.shutdownHook = new Thread(SHUTDOWN_HOOK_THREAD_NAME) {
         @Override
         public void run() {
            synchronized (startupShutdownMonitor) {
               // 回調去關閉容器
               doClose();
            }
         }
      };
      // 注冊鉤子
      Runtime.getRuntime().addShutdownHook(this.shutdownHook);
   }
}

注冊實現(xiàn)smartLifecycle的Bean

在創(chuàng)建 webserver 的時候,會創(chuàng)建一個實現(xiàn)smartLifecycle的 bean,用來支撐 server 的優(yōu)雅關閉。

org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext

private void createWebServer() {
      // 省略其他無關代碼
      this.webServer = factory.getWebServer(getSelfInitializer());
      // 注冊webServerGracefulShutdown用來實現(xiàn)server優(yōu)雅關閉
      getBeanFactory().registerSingleton("webServerGracefulShutdown",new WebServerGracefulShutdownLifecycle(this.webServer));
      // 省略其他無關代碼
}

可以看到 WebServerGracefulShutdownLifecycle 類實現(xiàn)SmartLifecycle接口,重寫了 stop 方法,stop 方法會觸發(fā) webserver 的優(yōu)雅關閉方法(取決于具體使用的 webserver 如 tomcatWebServer)。

org.springframework.boot.web.servlet.context.WebServerGracefulShutdownLifecycle

class WebServerGracefulShutdownLifecycle implements SmartLifecycle {
   @Override
   public void stop(Runnable callback) {
      this.running = false;
      // 優(yōu)雅關閉server
      this.webServer.shutDownGracefully((result) -> callback.run());
   }
}

org.springframework.boot.web.embedded.tomcat.TomcatWebServer

public class TomcatWebServer implements WebServer {
   public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
      this.tomcat = tomcat;
      this.autoStart = autoStart;
      // 如果SpringBoot開啟了優(yōu)雅停機配置,shutdown = Shutdown.GRACEFUL
      this.gracefulShutdown = (shutdown == Shutdown.GRACEFUL) ? new GracefulShutdown(tomcat) : null;
      initialize();
   }
   @Override
   public void shutDownGracefully(GracefulShutdownCallback callback) {
      if (this.gracefulShutdown == null) {
         // 如果沒有開啟優(yōu)雅停機,會立即關閉tomcat服務器
         callback.shutdownComplete(GracefulShutdownResult.IMMEDIATE);
         return;
      }
      // 優(yōu)雅關閉服務器
      this.gracefulShutdown.shutDownGracefully(callback);
   }
}

smartLifecycle的工作原理

上文提到鉤子方法被調用后會執(zhí)行 doColse()方法,在關閉容器之前,會通過 lifecycleProcessor 調用 lifecycle 的方法。

org.springframework.context.support.AbstractApplicationContext

protected void doClose() {
   if (this.active.get() && this.closed.compareAndSet(false, true)) {
      LiveBeansView.unregisterApplicationContext(this);
      // 發(fā)布 ContextClosedEvent 事件
      publishEvent(new ContextClosedEvent(this));
      // 回調所有實現(xiàn)Lifecycle 接口的Bean的stop方法
      if (this.lifecycleProcessor != null) {
            this.lifecycleProcessor.onClose();
      }
      // 銷毀bean, 關閉容器
      destroyBeans();
      closeBeanFactory();
      onClose();
      if (this.earlyApplicationListeners != null) {
         this.applicationListeners.clear();
         this.applicationListeners.addAll(this.earlyApplicationListeners);
      }
      // Switch to inactive.
      this.active.set(false);
   }
}

關閉 Lifecycle Bean 的入口: org.springframework.context.support.DefaultLifecycleProcessor

public class DefaultLifecycleProcessor implements LifecycleProcessor, BeanFactoryAware {
   @Override
   public void onClose() {
      stopBeans();
      this.running = false;
   }
   private void stopBeans() {
      //獲取所有的 Lifecycle bean
      Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
      //按Phase值對bean分組, 如果沒有實現(xiàn) Phased 接口則認為 Phase 是 0
      Map<Integer, LifecycleGroup> phases = new HashMap<>();
      lifecycleBeans.forEach((beanName, bean) -> {
         int shutdownPhase = getPhase(bean);
         LifecycleGroup group = phases.get(shutdownPhase);
         if (group == null) {
            group = new LifecycleGroup(shutdownPhase, this.timeoutPerShutdownPhase, lifecycleBeans, false);
            phases.put(shutdownPhase, group);
         }
         group.add(beanName, bean);
      });
      if (!phases.isEmpty()) {
         List<Integer> keys = new ArrayList<>(phases.keySet());
         //按照 Phase 值倒序
         keys.sort(Collections.reverseOrder());
         // Phase值越大優(yōu)先級越高,先執(zhí)行
         for (Integer key : keys) {
            phases.get(key).stop();
         }
      }
   }

DefaultLifecycleProcessor 的 stop 方法執(zhí)行流程:

  • 獲取容器中的所有實現(xiàn)了 Lifecycle 接口的 Bean。(smartLifecycle 接口繼承了 Lifecycle)
  • 再對包含所有 bean 的 List 分組按 phase 值倒序排序,值大的排前面。 (沒有實現(xiàn) Phased 接口, Phase 默認為0)
  • 依次調用各分組的里 bean 的 stop 方法 ( Phase 越大 stop 方法優(yōu)先執(zhí)行)

優(yōu)雅停機超時時間如何控制

從上文我們已經(jīng)可以梳理出,優(yōu)雅停機的執(zhí)行流程,下面可以看下停機超時時間是如何控制的。

org.springframework.context.support.DefaultLifecycleProcessor

// DefaultLifecycleProcessor內部類
private class LifecycleGroup {
   public void stop() {
      this.members.sort(Collections.reverseOrder());
      // count值默認為該組smartLifeCycel bean的數(shù)量
      CountDownLatch latch = new CountDownLatch(this.smartMemberCount);
      // 用于日志打印,打印等待超時未關閉成功的beanName
      Set<String> countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet<>());
      Set<String> lifecycleBeanNames = new HashSet<>(this.lifecycleBeans.keySet());
      for (LifecycleGroupMember member : this.members) {
         if (lifecycleBeanNames.contains(member.name)) {
            // bean如果還沒關閉,執(zhí)行關閉方法
            doStop(this.lifecycleBeans, member.name, latch, countDownBeanNames);
         }
         else if (member.bean instanceof SmartLifecycle) {
            // 如果是SmartLifecycle bean 并且已經(jīng)被提前處理了(依賴其他更優(yōu)先關閉的bean,會提前關閉)
            latch.countDown();
         }
      }
      try {
         // 等待該組 所有smartLifeCycel bean成功關閉 或者 超時
         // 等待時間默認30s, 如果沒有配置timeout-per-shutdown-phase
         latch.await(this.timeout, TimeUnit.MILLISECONDS);
      }
      catch (InterruptedException ex) {
         Thread.currentThread().interrupt();
      }
   }
}
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,
      final CountDownLatch latch, final Set<String> countDownBeanNames) {
   // 從未關閉的bean List中移除
   Lifecycle bean = lifecycleBeans.remove(beanName);
   if (bean != null) {
      String[] dependentBeans = getBeanFactory().getDependentBeans(beanName);
      // 如果該bean被其他bean依賴,優(yōu)先關閉那些bean
      for (String dependentBean : dependentBeans) {
         doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames);
      }
      // Lifecycel#isRunning 需要為true才會執(zhí)行stop方法
      if (bean.isRunning()) {
           if (bean instanceof SmartLifecycle) {
              // 關閉之前先記錄,如果超時沒關閉成功 用于打印日志提醒
              countDownBeanNames.add(beanName);
              ((SmartLifecycle) bean).stop(() -> {
                 // 執(zhí)行成功countDown
                 latch.countDown();
                 // 關閉成功移除
                 countDownBeanNames.remove(beanName);
              });
           }
           else {
               // 普通Lifecycle bean直接調用stop方法
               bean.stop();
           }
       }
       else if (bean instanceof SmartLifecycle) {
            // 如何SmartLifecycle不需要關閉,直接countDown
           latch.countDown();
       }
   }
}
  • DefaultLifecycleProcessor 利用 CountDownLatch 來控制等待bean的關閉方法執(zhí)行完畢,count=本組 SmartLifecycle bean 的數(shù)量,只有所有 SmartLifecycle 都執(zhí)行完,回調執(zhí)行 latch.countDown(),主線程才會結束等待,否則直到超時。
  • timeout-per-shutdown-phase: 30s, 該配置是針對每一組 Lifecycle bean 分別生效,不是所有的 Lifecycle bean,比如有2組不同puase 值的 bean, 會分別有最長 30s 等待時間。
  • 超時等待只對異步執(zhí)行 SmartLifecycle #stop(Runnable callback) 方法有效果,同步執(zhí)行沒有效果。
  • 如果不同組的 Lifecycle bean 之間有依賴關系,當前組 bean 被其他組的 bean 依賴,其他組的 bean 會先進行關閉(也會調用本輪生成 latch 對象的 countDown()),導致本輪的 latch.countDown()調用次數(shù)會超過初始化的 count 值,導致提前結束等待的情況發(fā)生。

優(yōu)雅停機的執(zhí)行流程總結:

  • SpringBoot 通過 Shutdown Hook 來注冊 doclose() 回調方法,在應用關閉的時候觸發(fā)執(zhí)行。
  • SpringBoot 在創(chuàng)建 webserver的時候,會注冊實現(xiàn) smartLifecycel 接口的 bean,用來優(yōu)雅關閉 tomcat
  • doClose()在銷毀 bean, 關閉容器之前會執(zhí)行所有實現(xiàn) Lifecycel 接口 bean 的 stop方法,并且會按 Phase 值分組, phase 大的優(yōu)先執(zhí)行。
  • WebServerGracefulShutdownLifecycle,Phase=Inter.MAX_VALUE,處于最優(yōu)先執(zhí)行序列,所以 tomcat 會先觸發(fā)優(yōu)雅關閉,并且tomcat 關閉方法是異步執(zhí)行的,主線會繼續(xù)調用執(zhí)行本組其他 bean 的關閉方法,然后等待所有 bean 關閉完畢,超過等待時間,會執(zhí)行下一組 Lifecycle bean 的關閉。

以上就是Spring Boot 優(yōu)雅停機原理詳解的詳細內容,更多關于Spring Boot 停機原理的資料請關注腳本之家其它相關文章!

相關文章

  • Java排序算法中的快速排序算法實現(xiàn)

    Java排序算法中的快速排序算法實現(xiàn)

    這篇文章主要介紹了Java排序算法中的快速排序算法實現(xiàn),通過一趟排序將要排序的數(shù)據(jù)分割成獨立的兩部分,其中一部分的所有數(shù)據(jù)都比另外一部分的所有數(shù)據(jù)都要小,然后再按此方法對這兩部分數(shù)據(jù)分別進行快速排序,需要的朋友可以參考下
    2023-12-12
  • Kafka的安裝及接入SpringBoot的詳細過程

    Kafka的安裝及接入SpringBoot的詳細過程

    Kafka 是一種高性能、分布式的消息隊列系統(tǒng),最初由 LinkedIn 公司開發(fā),并于2011年成為 Apache 頂級項目,這篇文章主要介紹了Kafka的安裝及接入SpringBoot,需要的朋友可以參考下
    2024-05-05
  • jedis的testWhileIdle用法源碼解讀

    jedis的testWhileIdle用法源碼解讀

    這篇文章主要為大家介紹了jedis的testWhileIdle用法源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Eclipse插件開發(fā)實現(xiàn)控制臺輸出信息的方法

    Eclipse插件開發(fā)實現(xiàn)控制臺輸出信息的方法

    今天小編就為大家分享一篇關于Eclipse插件開發(fā)實現(xiàn)控制臺輸出信息的方法,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • spring security中的csrf防御原理(跨域請求偽造)

    spring security中的csrf防御原理(跨域請求偽造)

    這篇文章主要介紹了spring security中的csrf防御機制原理解析(跨域請求偽造),本文通過實例代碼詳解的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12
  • java抓取鼠標事件和鼠標滾輪事件示例

    java抓取鼠標事件和鼠標滾輪事件示例

    這篇文章主要介紹了java抓取鼠標事件和鼠標滾輪事件示例,需要的朋友可以參考下
    2014-05-05
  • java中抽象類、抽象方法、接口與實現(xiàn)接口實例詳解

    java中抽象類、抽象方法、接口與實現(xiàn)接口實例詳解

    這篇文章主要給大家介紹了關于java中抽象類、抽象方法、接口與實現(xiàn)接口的相關資料,文中通過示例代碼將四者介紹的非常詳細,并且簡單介紹了抽象類和接口的區(qū)別,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。
    2017-11-11
  • Spring Boot 中整合 MyBatis-Plus詳細步驟(最新推薦)

    Spring Boot 中整合 MyBatis-Plus詳細步驟(最新推薦)

    本文詳細介紹了如何在SpringBoot項目中整合MyBatis-Plus,包括整合步驟、基本CRUD操作、分頁查詢、批量操作、自定義SQL操作等,通過這些步驟,開發(fā)者可以快速實現(xiàn)數(shù)據(jù)庫操作,提高開發(fā)效率,感興趣的朋友一起看看吧
    2025-01-01
  • Java程序打印奧林匹克標志方法詳解

    Java程序打印奧林匹克標志方法詳解

    這篇文章主要介紹了Java程序打印奧林匹克標志方法詳解,需要的朋友可以參考下。
    2017-09-09
  • SpringBoot項目使用jasypt加解密的方法

    SpringBoot項目使用jasypt加解密的方法

    jasypt是一個通用的加解密庫,我們可以使用它在配置文件中對數(shù)據(jù)庫密碼進行加密,以確保其安全性,接下來通過本文給大家介紹SpringBoot項目使用jasypt加解密的方法,感興趣的朋友一起看看吧
    2022-05-05

最新評論

临清市| 巢湖市| 尼木县| 上饶县| 永善县| 汉中市| 阿勒泰市| 襄汾县| 米脂县| 榆社县| 容城县| 平凉市| 庆阳市| 茶陵县| 张掖市| 凤山县| 清新县| 南陵县| 乐亭县| 绥芬河市| 民权县| 农安县| 蛟河市| 贺州市| 沙湾县| 象山县| 常德市| 富锦市| 吉林省| 赤壁市| 舒城县| 宾川县| 讷河市| 西吉县| 齐河县| 晋城| 湖州市| 罗甸县| 榆树市| 海宁市| 洛阳市|