日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
別再用kill-9了,這才是微服務(wù)上下線的正確姿勢!

 對于微服務(wù)來說,服務(wù)的優(yōu)雅上下線是必要的。

我們提供的服務(wù)有:成都做網(wǎng)站、成都網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、定安ssl等。為超過千家企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的定安網(wǎng)站制作公司

就上線來說,如果組件或者容器沒有啟動成功,就不應(yīng)該對外暴露服務(wù),對于下線來說,如果機器已經(jīng)停機了,就應(yīng)該保證服務(wù)已下線,如此可避免上游流量進入不健康的機器。

優(yōu)雅下線

基礎(chǔ)下線(Spring/SpringBoot/內(nèi)置容器)

首先JVM本身是支持通過shutdownHook的方式優(yōu)雅停機的。

 
 
 
 
  1. Runtime.getRuntime().addShutdownHook(new Thread() { 
  2.     @Override 
  3.     public void run() { 
  4.         close(); 
  5.     } 
  6. });

此方式支持在以下幾種場景優(yōu)雅停機:

程序正常退出

使用System.exit()

終端使用Ctrl+C

使用Kill pid干掉進程

那么如果你偏偏要kill -9 程序肯定是不知所措的。

而在Springboot中,其實已經(jīng)幫你實現(xiàn)好了一個shutdownHook,支持響應(yīng)Ctrl+c或者kill -15 TERM信號。

隨便啟動一個應(yīng)用,然后Ctrl+c一下,觀察日志就可知, 它在 AnnotationConfigEmbeddedWebApplicationContext 這個類中打印出了疑似Closing...的日志,真正的實現(xiàn)邏輯在其父類 AbstractApplicationContext 中(這個其實是spring中的類,意味著什么呢,在spring中就支持了對優(yōu)雅停機的擴展)。

Spring Boot 系列教程和示例源碼看這里:https://github.com/javastacks/spring-boot-best-practice

 
 
 
 
  1. public void registerShutdownHook() { 
  2.     if (this.shutdownHook == null) { 
  3.         this.shutdownHook = new Thread() { 
  4.             public void run() { 
  5.                 synchronized(AbstractApplicationContext.this.startupShutdownMonitor) { 
  6.                     AbstractApplicationContext.this.doClose(); 
  7.                 } 
  8.             } 
  9.         }; 
  10.         Runtime.getRuntime().addShutdownHook(this.shutdownHook); 
  11.     }
  12. public void destroy() { 
  13.     this.close(); 
  14. public void close() { 
  15.     Object var1 = this.startupShutdownMonitor;
  16.      synchronized(this.startupShutdownMonitor) {
  17.          this.doClose(); 
  18.         if (this.shutdownHook != null) { 
  19.             try { 
  20.                 Runtime.getRuntime().removeShutdownHook(this.shutdownHook); 
  21.             } catch (IllegalStateException var4) { 
  22.                 ; 
  23.             } 
  24.         } 
  25.     } 
  26. protected void doClose() { 
  27.     if (this.active.get() && this.closed.compareAndSet(false, true)) { 
  28.         if (this.logger.isInfoEnabled()) { 
  29.             this.logger.info("Closing " + this); 
  30.         } 
  31.         LiveBeansView.unregisterApplicationContext(this); 
  32.         try { 
  33.             this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this))); 
  34.         } catch (Throwable var3) { 
  35.             this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3); 
  36.         } 
  37.         if (this.lifecycleProcessor != null) { 
  38.             try { 
  39.                 this.lifecycleProcessor.onClose(); 
  40.             } catch (Throwable var2) { 
  41.                 this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2); 
  42.             } 
  43.         } 
  44.         this.destroyBeans(); 
  45.         this.closeBeanFactory(); 
  46.         this.onClose(); 
  47.         this.active.set(false); 
  48.     }  
  49. }

我們能對它做些什么呢,其實很明顯,在doClose方法中它發(fā)布了一個ContextClosedEvent的方法,不就是給我們擴展用的么。

于是我們可以寫個監(jiān)聽器監(jiān)聽ContextClosedEvent,在發(fā)生事件的時候做下線邏輯,對微服務(wù)來說即是從注冊中心中注銷掉服務(wù)。

 
 
 
 
  1. @Component 
  2. public class GracefulShutdownListener implements ApplicationListener { 
  3.         @Override 
  4.     public void onApplicationEvent(ContextClosedEvent contextClosedEvent){ 
  5.        //注銷邏輯 
  6.        zookeeperRegistry.unregister(mCurrentServiceURL); 
  7.        ... 
  8.     } 
  9. }

可能會有疑問的是,微服務(wù)中一般來說,注銷服務(wù)往往是優(yōu)雅下線的第一步,接著才會執(zhí)行停機操作,那么這個時候流量進來怎么辦呢?

個人會建議是,在注銷服務(wù)之后就可開啟請求擋板拒絕流量了,通過微服務(wù)框架本身的故障轉(zhuǎn)移功能去處理被拒絕的流量即可。另外,關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):面試,可以獲取我整理的 Java、Spring Boot 系列面試題和答案,非常齊全。

Docker中的下線

好有人說了,我用docker部署服務(wù),支不支持優(yōu)雅下線。

那來看看docker的一些停止命令都會干些啥:

一般來說,正常人可能會用docker stop或者docker kill 命令去關(guān)閉容器(當(dāng)然如果上一步注冊了USR2自定義信息,可能會通過docker exec kill -12去關(guān)閉)。

對于docker stop來說,它會發(fā)一個SIGTERM(kill -15 term信息)給容器的PID1進程,并且默認(rèn)會等待10s,再發(fā)送一個SIGKILL(kill -9 信息)給PID1。

那么很明顯,docker stop允許程序有個默認(rèn)10s的反應(yīng)時間去做一下優(yōu)雅停機的操作,程序只要能對kill -15 信號做些反應(yīng)就好了,如上一步描述。那么這是比較良好的方式。

當(dāng)然如果shutdownHook方法執(zhí)行了個50s,那肯定不優(yōu)雅了??梢酝ㄟ^docker stop -t 加上等待時間。

外置容器的shutdown腳本(Jetty)

如果非要用外置容器方式部署(個人認(rèn)為浪費資源并提升復(fù)雜度)。那么能不能優(yōu)雅停機呢。

可以當(dāng)然也是可以的,這里有兩種方式:

首先RPC框架本身提供優(yōu)雅上下線接口,以供調(diào)用來結(jié)束整個應(yīng)用的生命周期,并且提供擴展點供開發(fā)者自定義服務(wù)下線自身的停機邏輯。同時調(diào)用該接口的操作會封裝成一個preStop操作固化在jetty或者其他容器的shutdown腳本中,保證在容器停止之前先調(diào)用下線接口結(jié)束掉整個應(yīng)用的生命周期。shutdown腳本中執(zhí)行類發(fā)起下線服務(wù) -> 關(guān)閉端口 -> 檢查下線服務(wù)直至完成 -> 關(guān)閉容器的流程。

而更簡單的另一種方法是直接在腳本中加入kill -15命令。

優(yōu)雅上線

優(yōu)雅上線相對來說可能會更加困難一些,因為沒有什么默認(rèn)的實現(xiàn)方式,但是總之呢,一個原則就是確保端口存在之后才上線服務(wù)。

springboot內(nèi)置容器優(yōu)雅上線

這個就很簡單了,并且業(yè)界在應(yīng)用層面的優(yōu)雅上線均是在內(nèi)置容器的前提下實現(xiàn)的,并且還可以配合一些列健康檢查做文章。Spring Boot 優(yōu)雅關(guān)閉新姿勢,看看這篇。

參看sofa-boot的健康檢查的源碼,它會在程序啟動的時候先對springboot的組件做一些健康檢查,然后再對它自己搞得sofa的一些中間件做健康檢查,整個健康檢查的流程完畢之后(sofaboot 目前是沒法對自身應(yīng)用層面做健康檢查的,它有寫相關(guān)接口,但是寫死了port is ready...)才會暴露服務(wù)或者說優(yōu)雅上線,那么它健康檢查的時機是什么時候呢:

 
 
 
 
  1. @Override 
  2. public void onApplicationEvent(ContextRefreshedEvent event) { 
  3.     healthCheckerProcessor.init(); 
  4.     healthIndicatorProcessor.init(); 
  5.     afterHealthCheckCallbackProcessor.init(); 
  6.     publishBeforeHealthCheckEvent(); 
  7.     readinessHealthCheck(); 
  8. }

可以看到它是監(jiān)聽了ContextRefreshedEvent這個事件。在內(nèi)置容器模式中,內(nèi)置容器模式的start方法是在refreshContext方法中,方法執(zhí)行完成之后發(fā)布一個ContextRefreshedEvent事件,也就是說在監(jiān)聽到這個事件的時候,內(nèi)置容器必然是啟動成功了的。

但ContextRefreshedEvent這個事件,在一些特定場景中由于種種原因,ContextRefreshedEvent會被監(jiān)聽到多次,沒有辦法保證當(dāng)前是最后一次event,從而正確執(zhí)行優(yōu)雅上線邏輯。

在springboot中還有一個更加靠后的事件,叫做ApplicationReadyEvent,它的發(fā)布藏在了afterRefresh還要后面的那一句listeners.finished(context, null)中,完完全全可以保證內(nèi)置容器 端口已經(jīng)存在了,所以我們可以監(jiān)聽這個事件去做優(yōu)雅上線的邏輯,甚至可以把中間件相關(guān)的健康檢查集成在這里。

 
 
 
 
  1. @Component 
  2. public class GracefulStartupListener implements ApplicationListener {     
  3.     @Override 
  4.     public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent){ 
  5.        //注冊邏輯 優(yōu)雅上線 
  6.        apiRegister.register(urls); 
  7.        ... 
  8.     } 
  9. }

外置容器(Jetty)優(yōu)雅上線

目前大多數(shù)應(yīng)用的部署模式不管是jetty部署模式還是docker部署模式(同樣使用jetty鏡像),本質(zhì)上用的都是外置容器。那么這個情況就比較困難了,至少在應(yīng)用層面無法觀察到外部容器的運行狀態(tài),并且容器本身沒有提供什么hook給你實現(xiàn)。

那么和優(yōu)雅上線一樣,需要RPC框架提供優(yōu)雅上線接口來初始化整個應(yīng)用的生命周期,并且提供擴展點給開發(fā)者供執(zhí)行自定義的上線邏輯(上報版本探測信息等)。同樣將調(diào)用這個接口封裝成一個postStart操作,固化在jetty等外置容器的startup腳本中,保證應(yīng)用在容器啟動之后在上線。容器執(zhí)行類似啟動容器 -> 健康檢查 -> 上線服務(wù)邏輯 -> 健康上線服務(wù)直至完成 的流程。


文章題目:別再用kill-9了,這才是微服務(wù)上下線的正確姿勢!
瀏覽地址:http://www.5511xx.com/article/coghdgc.html