<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          微服務(wù)之:服務(wù)掛的太干脆,Nacos還沒(méi)反應(yīng)過(guò)來(lái),怎么辦?

          共 5889字,需瀏覽 12分鐘

           ·

          2021-07-04 17:49

          前言

          我們知道通過(guò)Nacos等注冊(cè)中心可以實(shí)現(xiàn)微服務(wù)的治理。但引入了Nacos之后,真的就像理想中那樣所有服務(wù)都由Nacos來(lái)完美的管理了嗎?Too young,too simple!

          今天這篇文章就跟大家聊聊,當(dāng)服務(wù)異常宕機(jī),Nacos還未反應(yīng)過(guò)來(lái)時(shí),可能會(huì)發(fā)生的狀況以及現(xiàn)有的解決方案。

          Nacos的健康檢查

          故事還要從Nacos對(duì)服務(wù)實(shí)例的健康檢查說(shuō)起。

          Nacos目前支持臨時(shí)實(shí)例使用心跳上報(bào)方式維持活性。Nacos客戶端會(huì)維護(hù)一個(gè)定時(shí)任務(wù),每隔5秒發(fā)送一次心跳請(qǐng)求,以確保自己處于活躍狀態(tài)。

          Nacos服務(wù)端在15秒內(nèi)如果沒(méi)收到客戶端的心跳請(qǐng)求,會(huì)將該實(shí)例設(shè)置為不健康,在30秒內(nèi)沒(méi)收到心跳,會(huì)將這個(gè)臨時(shí)實(shí)例摘除。

          如果服務(wù)突然掛掉

          在正常業(yè)務(wù)場(chǎng)景下,如果關(guān)閉掉一個(gè)服務(wù)實(shí)例,默認(rèn)情況下會(huì)在關(guān)閉之前主動(dòng)調(diào)用注銷接口,將Nacos服務(wù)端注冊(cè)的實(shí)例清除掉。

          如果服務(wù)實(shí)例還沒(méi)來(lái)得注銷已經(jīng)被干掉,比如正常kill一個(gè)應(yīng)用,應(yīng)用會(huì)處理完手頭的事情再關(guān)閉,但如果使用kill -9來(lái)強(qiáng)制殺掉,就會(huì)出現(xiàn)無(wú)法注銷的情況。

          針對(duì)這種意外情況,服務(wù)注銷接口是無(wú)法被正確調(diào)用的,此時(shí)就需要健康檢查來(lái)確保該實(shí)例被刪除。

          通過(guò)上面分析的Nacos健康檢查機(jī)制,我們會(huì)發(fā)現(xiàn)服務(wù)突然掛掉之后,會(huì)有15秒的間隙。在這段時(shí)間,Nacos服務(wù)端還沒(méi)感知到服務(wù)掛掉,依舊將該服務(wù)提供給客戶端使用。

          此時(shí),必然會(huì)有一部分請(qǐng)求被分配到異常的實(shí)例上。針對(duì)這種情況,又該如何處理呢?如何確保服務(wù)不影響正常的業(yè)務(wù)呢?

          自定義心跳周期

          針對(duì)上面的問(wèn)題,我們最容易想到的是解決方案就是縮短默認(rèn)的健康檢查時(shí)間。

          原本15秒才能發(fā)現(xiàn)服務(wù)異常,標(biāo)記為不健康,那么是否可以將其縮短呢?這樣錯(cuò)誤影響的范圍便可以變小,變得可控。

          針對(duì)此,Nacos 1.1.0之后提供了自定義心跳周期的配置。如果你基于客戶端進(jìn)行操作,在創(chuàng)建實(shí)例時(shí),可在實(shí)例的metadata數(shù)據(jù)中進(jìn)行心跳周期、健康檢查過(guò)期時(shí)間及刪除實(shí)例時(shí)間的配置。

          相關(guān)示例如下:

          String serviceName = randomDomainName();

          Instance instance = new Instance();
          instance.setIp("1.1.1.1");
          instance.setPort(9999);
          Map<String, String> metadata = new HashMap<String, String>();
          // 設(shè)置心跳的周期,單位為毫秒
          metadata.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "3000");
          // 設(shè)置心跳超時(shí)時(shí)間,單位為毫秒;服務(wù)端6秒收不到客戶端心跳,會(huì)將該客戶端注冊(cè)的實(shí)例設(shè)為不健康:
          metadata.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "6000");
          // 設(shè)置實(shí)例刪除的超時(shí)時(shí)間,單位為毫秒;即服務(wù)端9秒收不到客戶端心跳,會(huì)將該客戶端注冊(cè)的實(shí)例刪除:
          metadata.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "9000");
          instance.setMetadata(metadata);

          naming.registerInstance(serviceName, instance);

          如果是基于Spring Cloud Alibaba的項(xiàng)目,可通過(guò)如下方式配置:

          spring:
          application:
          name: user-service-provider
          cloud:
          nacos:
          discovery:
          server-addr: 127.0.0.1:8848
          heart-beat-interval: 1000 #心跳間隔。單位為毫秒。
          heart-beat-timeout: 3000 #心跳暫停。單位為毫秒。
          ip-delete-timeout: 6000 #Ip刪除超時(shí)。單位為毫秒。

          在某些Spring Cloud版本中,上述配置可能無(wú)法生效。也可以直接配置metadata的數(shù)據(jù)。配置方式如下:

          spring:
          application:
          name: user-service-provider
          cloud:
          nacos:
          discovery:
          server-addr: 127.0.0.1:8848
          metadata:
          preserved.heart.beat.interval: 1000 #心跳間隔。時(shí)間單位:毫秒。
          preserved.heart.beat.timeout: 3000 #心跳暫停。時(shí)間單位:毫秒。即服務(wù)端6秒收不到客戶端心跳,會(huì)將該客戶端注冊(cè)的實(shí)例設(shè)為不健康;
          preserved.ip.delete.timeout: 6000 #Ip刪除超時(shí)。時(shí)間單位:秒。即服務(wù)端9秒收不到客戶端心跳,會(huì)將該客戶端注冊(cè)的實(shí)例刪除;

          其中第一種配置,感興趣的朋友可以看一下NacosServiceRegistryAutoConfiguration中相關(guān)組件的實(shí)例化。在某些版本中由于NacosRegistration和NacosDiscoveryProperties實(shí)例化的順序問(wèn)題會(huì)導(dǎo)致配置未生效。此時(shí)可考慮第二種配置形式。

          上面的配置項(xiàng),最終會(huì)在NacosServiceRegistry在進(jìn)行實(shí)例注冊(cè)時(shí)通過(guò)getNacosInstanceFromRegistration方法進(jìn)行封裝:

          private Instance getNacosInstanceFromRegistration(Registration registration) {
          Instance instance = new Instance();
          instance.setIp(registration.getHost());
          instance.setPort(registration.getPort());
          instance.setWeight(nacosDiscoveryProperties.getWeight());
          instance.setClusterName(nacosDiscoveryProperties.getClusterName());
          instance.setEnabled(nacosDiscoveryProperties.isInstanceEnabled());
          // 設(shè)置Metadata
          instance.setMetadata(registration.getMetadata());
          instance.setEphemeral(nacosDiscoveryProperties.isEphemeral());
          return instance;
          }

          其中setMetadata方法即是。

          通過(guò)Nacos提供的心跳周期配置,再結(jié)合自身的業(yè)務(wù)場(chǎng)景,我們就可以選擇最適合的心跳檢測(cè)機(jī)制,盡最大可能避免對(duì)業(yè)務(wù)的影響。

          這個(gè)方案看起來(lái)心跳周期短越好,但這樣會(huì)對(duì)Nacos服務(wù)端造成一定的壓力。如果服務(wù)器允許,還是可以盡量縮短的。

          Nacos的保護(hù)閾值

          在上述配置中,我們還要結(jié)合自身的項(xiàng)目情況考慮一下Nacos保護(hù)閾值的配置。

          在Nacos中針對(duì)注冊(cè)的服務(wù)實(shí)例有一個(gè)保護(hù)閾值的配置項(xiàng)。該配置項(xiàng)的值為0-1之間的浮點(diǎn)數(shù)。

          本質(zhì)上,保護(hù)閾值是?個(gè)?例值(當(dāng)前服務(wù)健康實(shí)例數(shù)/當(dāng)前服務(wù)總實(shí)例數(shù))。

          ?般流程下,服務(wù)消費(fèi)者要從Nacos獲取可?實(shí)例有健康/不健康狀態(tài)之分。Nacos在返回實(shí)例時(shí),只會(huì)返回健康實(shí)例。

          但在?并發(fā)、?流量場(chǎng)景會(huì)存在?定的問(wèn)題。比如,服務(wù)A有100個(gè)實(shí)例,98個(gè)實(shí)例都處于不健康狀態(tài),如果Nacos只返回這個(gè)健康實(shí)例的話。流量洪峰的到來(lái)可能會(huì)直接打垮這兩個(gè)服務(wù),進(jìn)一步產(chǎn)生雪崩效應(yīng)。

          保護(hù)閾值存在的意義在于當(dāng)服務(wù)A健康實(shí)例數(shù)/總實(shí)例數(shù) < 保護(hù)閾值時(shí),說(shuō)明健康的實(shí)例不多了,保護(hù)閾值會(huì)被觸發(fā)(狀態(tài)true)。

          Nacos會(huì)把該服務(wù)所有的實(shí)例信息(健康的+不健康的)全部提供給消費(fèi)者,消費(fèi)者可能訪問(wèn)到不健康的實(shí)例,請(qǐng)求失敗,但這樣也?造成雪崩要好。犧牲了?些請(qǐng)求,保證了整個(gè)系統(tǒng)的可?。

          在上面的解決方案中,我們提到了可以自定義心跳周期,其中能夠看到實(shí)例的狀態(tài)會(huì)由健康、不健康和移除。這些參數(shù)的定義也要考慮到保護(hù)閾值的觸發(fā),避免雪崩效應(yīng)的發(fā)生。

          SpringCloud的請(qǐng)求重試

          即便上面我們對(duì)心跳周期進(jìn)行了調(diào)整,但在某一實(shí)例發(fā)生故障時(shí),還會(huì)有短暫的時(shí)間出現(xiàn)Nacos服務(wù)沒(méi)來(lái)得及將異常實(shí)例剔除的情況。此時(shí),如果消費(fèi)端請(qǐng)求該實(shí)例,依然會(huì)出現(xiàn)請(qǐng)求失敗。

          為了構(gòu)建更為健壯的應(yīng)用系統(tǒng),我們希望當(dāng)請(qǐng)求失敗的時(shí)候能夠有一定策略的重試機(jī)制,而不是直接返回失敗。這個(gè)時(shí)候就需要開(kāi)發(fā)人來(lái)實(shí)現(xiàn)重試機(jī)制。

          在微服務(wù)架構(gòu)中,通常我們會(huì)基于Ribbon或Spring Cloud LoadBalancer來(lái)進(jìn)行負(fù)載均衡處理。除了像Ribbon、Feign框架自身已經(jīng)支持的請(qǐng)求重試和請(qǐng)求轉(zhuǎn)移功能。Spring Cloud也提供了標(biāo)準(zhǔn)的loadbalancer相關(guān)配置。

          關(guān)于Ribbon框架的使用我們?cè)谶@里就不多說(shuō)了,重點(diǎn)來(lái)看看Spring Cloud是如何幫我們實(shí)現(xiàn)的。

          異常模擬

          我們先來(lái)模擬一下異常情況,將上面講到的先將上面的心跳周期調(diào)大,以方便測(cè)試。

          然后啟動(dòng)兩個(gè)provider和一個(gè)consumer服務(wù),負(fù)載均衡基于Spring Cloud LoadBalancer來(lái)處理。此時(shí)通過(guò)consumer進(jìn)行請(qǐng)求,你會(huì)發(fā)現(xiàn)LoadBalancer通過(guò)輪訓(xùn)來(lái)將請(qǐng)求均勻的分配到兩個(gè)provider上(打印日志)。

          此時(shí),通過(guò)kill -9命令將其中一個(gè)provider關(guān)掉。此時(shí),再通過(guò)consumer進(jìn)行請(qǐng)求,會(huì)發(fā)現(xiàn)成功一次,失敗一次,這樣交替出現(xiàn)。

          解決方案

          我們通過(guò)Spring Cloud提供的LoadBalancerProperties配置類中定義的配置項(xiàng)來(lái)對(duì)重試機(jī)制進(jìn)行配置,詳細(xì)的配置項(xiàng)目可以對(duì)照該類的屬性。

          在consumer的application配置中添加retry相關(guān)配置:

          spring:
          application:
          name: user-service-consumer
          cloud:
          nacos:
          discovery:
          server-addr: 127.0.0.1:8848
          loadbalancer:
          retry:
          # 開(kāi)啟重試
          enabled: true
          # 同一實(shí)例最大嘗試次數(shù)
          max-retries-on-same-service-instance: 1
          # 其他實(shí)例最大嘗試次數(shù)
          max-retries-on-next-service-instance: 2
          # 所有操作開(kāi)啟重試(慎重使用,特別是POST提交,冪等性保障)
          retry-on-all-operations: true

          上述配置中默認(rèn)retry是開(kāi)啟的。

          max-retries-on-same-service-instance指的是當(dāng)前實(shí)例嘗試的次數(shù),包括第一次請(qǐng)求,這里配置為1,也就是第一次請(qǐng)求失敗就轉(zhuǎn)移到其他實(shí)例了。當(dāng)然也可以配置大于1的數(shù)值,這樣還會(huì)在當(dāng)前實(shí)例再嘗試一下。

          max-retries-on-next-service-instance配置的轉(zhuǎn)移請(qǐng)求其他實(shí)例時(shí)最大嘗試次數(shù)。

          retry-on-all-operations默認(rèn)為false,也就是說(shuō)只支持Get請(qǐng)求的重試。這里設(shè)置為true支持所有的重試。既然涉及到重試,就需要保證好業(yè)務(wù)的冪等性。

          當(dāng)進(jìn)行上述配置之后,再次演示異常模擬,會(huì)發(fā)現(xiàn)即使服務(wù)掛掉,在Nacos中還存在,依舊可以正常進(jìn)行業(yè)務(wù)處理。

          關(guān)于Ribbon或其他同類組件也有類似的解決方案,大家可以相應(yīng)調(diào)研一下。

          解決方案的坑

          在使用Spring Cloud LoadBalancer時(shí)其實(shí)有一個(gè)坑,你可能會(huì)遇到上述配置不生效的情況。這是為什么呢?

          其實(shí)是因?yàn)橐蕾囈氲膯?wèn)題,Spring Cloud LoadBalancer的重試機(jī)制是基于spring-retry的,如果沒(méi)有引入對(duì)應(yīng)的依賴,便會(huì)導(dǎo)致配置無(wú)法生效。而官方文檔業(yè)務(wù)未給出說(shuō)明。

          <dependency>
          <groupId>org.springframework.retry</groupId>
          <artifactId>spring-retry</artifactId>
          </dependency>

          另外,上述實(shí)例是基于Spring Cloud 2020.0.0版本,其他版本可能有不同的配置。

          小結(jié)

          在使用微服務(wù)的時(shí)候并不是將Spring Cloud的組件集成進(jìn)去就完事了。這篇文章我們可以看到即便集成了Nacos,還會(huì)因?yàn)樾奶鴻C(jī)制來(lái)進(jìn)行一些折中處理,比如調(diào)整心跳頻次。

          同時(shí),即便調(diào)整了心跳參數(shù),還需要利用其它組件來(lái)兼顧請(qǐng)求異常時(shí)的重試和防止系統(tǒng)雪崩的發(fā)生。關(guān)注一下吧,持續(xù)更新微服務(wù)系列實(shí)戰(zhàn)內(nèi)容。


          往期推薦

          微服務(wù)之吐槽一下Nacos日志的瘋狂輸出

          還沒(méi)用過(guò)建造者模式?這篇文章幫你搞定

          軟件架構(gòu)分層,你的項(xiàng)目處于什么階段?

          一篇文章,只用看三遍,終生不忘網(wǎng)絡(luò)分層!

          一篇文章,領(lǐng)略Java8中ConcurrentHashMap的方方面面



          如果你覺(jué)得這篇文章不錯(cuò),那么,下篇通常會(huì)更好。添加微信好友,可備注“加群”(微信號(hào):zhuan2quan)

          一篇文章就看透技術(shù)本質(zhì)的人,
            和花一輩子都看不清的人,
            注定是截然不同的搬磚生涯。
          ▲ 長(zhǎng)按關(guān)注”程序新視界“,洞察技術(shù)內(nèi)幕
          瀏覽 130
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国内一级免费黄色视频在线网展览器的封 | 九九热视频精品在线 | 樱桃视频91 | 色婷婷色丁香五月天 | 五月婷婷第四色 |