日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Ribbon服务器状态:ServerStats及其断路器原理

發(fā)布時(shí)間:2024/4/13 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Ribbon服务器状态:ServerStats及其断路器原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

目錄

前言

正文

統(tǒng)計(jì)數(shù)據(jù)/屬性

成員方法

狀態(tài)/指標(biāo)信息使用場(chǎng)景舉例

默認(rèn)值不合理

代碼示例

總結(jié)


前言

我們知道Ribbon它是一個(gè)客戶端負(fù)載均衡器,因此它內(nèi)部維護(hù)著一個(gè)服務(wù)器列表ServerList,當(dāng)實(shí)例出現(xiàn)問題時(shí)候,需要將這部分異常的服務(wù)Server從負(fù)載均衡列表中T除掉,那么Ribbon是以什么作為參考,決定T除/不T除Server的呢???這就是本文將要講述的服務(wù)器狀態(tài)的管理:ServerStats。

負(fù)載均衡LB需要依賴這些統(tǒng)計(jì)信息做為判斷的策略,負(fù)載均衡器的統(tǒng)計(jì)類主要是LoadBalancerStats,其內(nèi)部持有ServerStats對(duì)每個(gè)Server的運(yùn)行情況做了相關(guān)統(tǒng)計(jì)如:平均響應(yīng)時(shí)間、累計(jì)失敗數(shù)、熔斷(時(shí)間)控制等。


正文

Stat中文釋義:統(tǒng)計(jì),Statistic單詞的簡(jiǎn)寫形式。另外,希望讀者在閱讀本文之前,已經(jīng)了解了netflix-statistics的知識(shí),你可以參考這篇文章:[享學(xué)Netflix] 四十四、netflix-statistics詳解,手把手教你寫個(gè)超簡(jiǎn)版監(jiān)控系統(tǒng)

服務(wù)狀態(tài)。在LoadBalancer中捕獲每個(gè)服務(wù)器(節(jié)點(diǎn))的各種狀態(tài),每個(gè)Server就對(duì)應(yīng)著一個(gè)ServerStats實(shí)例。ServerStats表示一臺(tái)Server的狀態(tài),各種緯度的統(tǒng)計(jì)數(shù)據(jù)才能使得你最終挑選出一個(gè)最適合的Server供以使用,以及計(jì)算其當(dāng)前訪問壓力(并發(fā)數(shù))、成功數(shù)、失敗數(shù)、是否熔斷、熔斷了多久等等。


統(tǒng)計(jì)數(shù)據(jù)/屬性

到底統(tǒng)計(jì)了哪些數(shù)據(jù)呢?對(duì)Server進(jìn)行多維度的數(shù)據(jù)統(tǒng)計(jì),均體現(xiàn)在它的成員屬性上:

public class ServerStats {private final CachedDynamicIntProperty connectionFailureThreshold;private final CachedDynamicIntProperty circuitTrippedTimeoutFactor;private final CachedDynamicIntProperty maxCircuitTrippedTimeout;private static final DynamicIntProperty activeRequestsCountTimeout = DynamicPropertyFactory.getInstance().getIntProperty("niws.loadbalancer.serverStats.activeRequestsCount.effectiveWindowSeconds", 60 * 10);long failureCountSlidingWindowInterval = 1000; private MeasuredRate serverFailureCounts = new MeasuredRate(failureCountSlidingWindowInterval);private MeasuredRate requestCountInWindow = new MeasuredRate(300000L);Server server;AtomicLong totalRequests = new AtomicLong();AtomicInteger successiveConnectionFailureCount = new AtomicInteger(0);AtomicInteger activeRequestsCount = new AtomicInteger(0);AtomicInteger openConnectionsCount = new AtomicInteger(0);private volatile long lastConnectionFailedTimestamp;private volatile long lastActiveRequestsCountChangeTimestamp;private AtomicLong totalCircuitBreakerBlackOutPeriod = new AtomicLong(0);private volatile long lastAccessedTimestamp;private volatile long firstConnectionTimestamp = 0; }

對(duì)這些統(tǒng)計(jì)數(shù)據(jù)/屬性分別做如下解釋說明:

  • connectionFailureThreshold:連接失敗閾值,默認(rèn)值3(超過就熔斷)
    • 默認(rèn)值配置:niws.loadbalancer.default.connectionFailureCountThreshold此key指定
    • 個(gè)性化配置:"niws.loadbalancer." + name + ".connectionFailureCountThreshold"
  • circuitTrippedTimeoutFactor:斷路器超時(shí)因子,默認(rèn)值10s。
    • 默認(rèn)值配置: niws.loadbalancer.default.circuitTripTimeoutFactorSeconds
    • 個(gè)性化配置:"niws.loadbalancer." + name + ".circuitTripTimeoutFactorSeconds"
  • maxCircuitTrippedTimeout:斷路器最大超時(shí)秒數(shù)(默認(rèn)使用超時(shí)因子計(jì)算出來),默認(rèn)值是30s。
    • 默認(rèn)值配置:niws.loadbalancer.default.circuitTripMaxTimeoutSeconds
    • 個(gè)性化配置:"niws.loadbalancer." + name + ".circuitTripMaxTimeoutSeconds"
  • ?
  • totalRequests:總請(qǐng)求數(shù)量。每次請(qǐng)求結(jié)束/錯(cuò)誤時(shí)就會(huì)+1。
  • successiveConnectionFailureCount:連續(xù)(successive)請(qǐng)求異常數(shù)量(這個(gè)連續(xù)發(fā)生在Retry重試期間)。
    • 在重試期間,但凡有一次成功了,就會(huì)把此參數(shù)置為0(失敗的話此參數(shù)就一直加)
    • 說明:只有在異常類型是callErrorHandler.isCircuitTrippingException(e)的時(shí)候,才會(huì)算作失敗,才會(huì)+1
      • 默認(rèn)情況下只有SocketException/SocketTimeoutException這兩種異常才算失敗哦~
  • activeRequestsCount:活躍請(qǐng)求數(shù)量(正在請(qǐng)求的數(shù)量,它能反應(yīng)該Server的負(fù)載、壓力)。
    • 但凡只要開始執(zhí)行Sever了,就+1
    • 但凡只要請(qǐng)求完成了/出錯(cuò)了,就-1
    • 注意:它有時(shí)間窗口的概念,后面講具體邏輯
  • openConnectionsCount:暫無任何使用處,可忽略。
  • ?
  • lastConnectionFailedTimestamp:最后一次失敗的時(shí)間戳。至于什么叫失敗,參考successiveConnectionFailureCount對(duì)失敗的判斷邏輯
  • lastActiveRequestsCountChangeTimestamp:簡(jiǎn)單的說就是activeRequestsCount的值最后變化的時(shí)間戳
  • totalCircuitBreakerBlackOutPeriod:斷路器斷電總時(shí)長(zhǎng)(連續(xù)失敗>=3次,增加20~30秒。具體增加多少秒,后面有計(jì)算邏輯)。
  • lastAccessedTimestamp:最后訪問時(shí)間戳。和lastActiveRequestsCountChangeTimestamp的區(qū)別是,它增/減都update一下,而lastAccessedTimestamp只有在增的時(shí)候才會(huì)update一下。
  • firstConnectionTimestamp:首次連接時(shí)間戳,只會(huì)記錄首次請(qǐng)求進(jìn)來時(shí)的時(shí)間。
  • ?
  • failureCountSlidingWindowInterval:失敗次數(shù)統(tǒng)計(jì)時(shí)間窗。默認(rèn)值1000ms
  • serverFailureCounts:上一秒失敗次數(shù)(上一秒是因?yàn)閒ailureCountSlidingWindowInterval默認(rèn)自是1000ms)
    • successiveConnectionFailureCount增它就增,只不過它有時(shí)間窗口(1s)
  • requestCountInWindow:一個(gè)窗口期內(nèi)的請(qǐng)求總數(shù),窗口期默認(rèn)為5分鐘(300秒)
    • activeRequestsCount增它就增,只不過它有時(shí)間窗口(300s)

當(dāng)然,它還有幾個(gè)基于netflix-statistics數(shù)據(jù)統(tǒng)計(jì)的指標(biāo)屬性:

ServerStats:// 默認(rèn)60s(1分鐘)publish一次數(shù)據(jù)private static final int DEFAULT_PUBLISH_INTERVAL = 60 * 1000; // = 1 minute// 緩沖區(qū)大小。這個(gè)默認(rèn)大小可謂非常大呀,就算你QPS是1000,也能抗1分鐘private static final int DEFAULT_BUFFER_SIZE = 60 * 1000; // = 1000 requests/sec for 1 minuteint bufferSize = DEFAULT_BUFFER_SIZE;int publishInterval = DEFAULT_PUBLISH_INTERVAL;private static final double[] PERCENTS = makePercentValues();private DataDistribution dataDist = new DataDistribution(1, PERCENTS);private DataPublisher publisher = null;private final Distribution responseTimeDist = new Distribution();
  • PERCENTS:百分比,可參見枚舉類Percent:[10,20…,90…,99.5]
  • dataDist:它是一個(gè)DataAccumulator,數(shù)據(jù)累加器。
  • publisher:定時(shí)publish發(fā)布數(shù)據(jù),默認(rèn)1分鐘發(fā)布一次
  • responseTimeDist:它是個(gè)Distribution類型,因?yàn)樗鼉H僅只需要持續(xù)累加數(shù)據(jù),然后提供最大最小值、平均值的訪問而已

dataDist和responseTimeDist統(tǒng)一通過noteResponseTime(double msecs)來記錄每個(gè)請(qǐng)求的響應(yīng)時(shí)間,dataDist按照時(shí)間窗口統(tǒng)計(jì),responseTimeDist一直累加


成員方法

已經(jīng)知道了每個(gè)字段的含義,再來看其提供的方法,就輕松很多了。

ServerStats:// 默認(rèn)構(gòu)造器:connectionFailureThreshold等參數(shù)均使用默認(rèn)值 該構(gòu)造器默認(rèn)無人調(diào)用public ServerStats() { ... }// 參數(shù)值來自于lbStats,可以和ClientName掛上鉤// 它在LoadBalancerStats#createServerStats()方法里被唯一調(diào)用public ServerStats(LoadBalancerStats lbStats) { ... }// 初始化對(duì)象,開始數(shù)據(jù)收集和報(bào)告。**請(qǐng)務(wù)必調(diào)用此方法** 它才是一個(gè)完整的實(shí)例public void initialize(Server server) {serverFailureCounts = new MeasuredRate(failureCountSlidingWindowInterval);requestCountInWindow = new MeasuredRate(300000L);if (publisher == null) {dataDist = new DataDistribution(getBufferSize(), PERCENTS);publisher = new DataPublisher(dataDist, getPublishIntervalMillis());// 啟動(dòng)任務(wù):開始發(fā)布數(shù)據(jù)。1分鐘發(fā)布一次publisher.start();}// 和Server關(guān)聯(lián)this.server = server;}// 停止數(shù)據(jù)方法public void close() {if (publisher != null)publisher.stop();}// 收集每一次請(qǐng)求的響應(yīng)時(shí)間public void noteResponseTime(double msecs){dataDist.noteValue(msecs);responseTimeDist.noteValue(msecs);}// 獲得當(dāng)前時(shí)間的活躍請(qǐng)求數(shù)(也就是Server的當(dāng)前負(fù)載)public int getActiveRequestsCount() {return getActiveRequestsCount(System.currentTimeMillis());}// 強(qiáng)調(diào):如果當(dāng)前時(shí)間currentTime距離上一次請(qǐng)求進(jìn)來已經(jīng)超過了時(shí)間窗口60s,那就返回0// 簡(jiǎn)單一句話:如果上次請(qǐng)求距今1分鐘了,那就一個(gè)請(qǐng)求都不算(強(qiáng)制歸零)public int getActiveRequestsCount(long currentTime) {int count = activeRequestsCount.get();if (count == 0) {return 0;} else if (currentTime - lastActiveRequestsCountChangeTimestamp > activeRequestsCountTimeout.get() * 1000 || count < 0) {activeRequestsCount.set(0);return 0; } else {return count;}}

這些是ServerStats提供的基本方法,能訪問到所有的成員屬性。下面介紹分別介紹兩個(gè)主題方法:


CircuitBreaker斷路器的原理

本處的斷路器解釋:當(dāng)有某個(gè)服務(wù)存在多個(gè)實(shí)例時(shí),在請(qǐng)求的過程中,負(fù)載均衡器會(huì)統(tǒng)計(jì)每次請(qǐng)求的情況(請(qǐng)求響應(yīng)時(shí)間,是否發(fā)生網(wǎng)絡(luò)異常等),當(dāng)出現(xiàn)了請(qǐng)求出現(xiàn)累計(jì)重試時(shí),負(fù)載均衡器會(huì)標(biāo)識(shí)當(dāng)前服務(wù)實(shí)例,設(shè)置當(dāng)前服務(wù)實(shí)例的斷路的時(shí)間區(qū)間,在此區(qū)間內(nèi),當(dāng)請(qǐng)求過來時(shí),負(fù)載均衡器會(huì)將此服務(wù)實(shí)例從可用服務(wù)實(shí)例列表中暫時(shí)剔除(其實(shí)就是暫時(shí)忽略此Server),優(yōu)先選擇其他服務(wù)實(shí)例。

該斷路器和Hystrix無任何關(guān)系,無任何關(guān)系,無任何關(guān)系。它是ServerStats內(nèi)部維護(hù)的一套熔斷機(jī)制,體現(xiàn)在如下方法上:

ServerStats:// 看看該斷路器到哪個(gè)時(shí)間點(diǎn)戒指(關(guān)閉)的時(shí)刻時(shí)間戳// 比如斷路器要從0點(diǎn)開30s,那么返回值就是00:00:30s這個(gè)時(shí)間戳唄private long getCircuitBreakerTimeout() {long blackOutPeriod = getCircuitBreakerBlackoutPeriod();if (blackOutPeriod <= 0) {return 0;}return lastConnectionFailedTimestamp + blackOutPeriod;}// 返回需要中斷的持續(xù)時(shí)間(毫秒值)private long getCircuitBreakerBlackoutPeriod() {int failureCount = successiveConnectionFailureCount.get();int threshold = connectionFailureThreshold.get();if (failureCount < threshold) {return 0;}int diff = (failureCount - threshold) > 16 ? 16 : (failureCount - threshold);int blackOutSeconds = (1 << diff) * circuitTrippedTimeoutFactor.get();if (blackOutSeconds > maxCircuitTrippedTimeout.get()) {blackOutSeconds = maxCircuitTrippedTimeout.get();}return blackOutSeconds * 1000L;}

目前斷路器統(tǒng)計(jì)失敗是靠連續(xù)失敗次數(shù)去判斷斷路邏輯的。此方法邏輯可總結(jié)如下:

  • 連續(xù)失敗次數(shù)還小于閾值(默認(rèn)3次),那么就不用斷路。否則打開斷路,執(zhí)行計(jì)算要斷開多久的邏輯
  • 計(jì)算失敗基數(shù),最大不能超過16(就算你連續(xù)失敗100次,此基數(shù)也是16)
  • 根據(jù)超時(shí)因子circuitTrippedTimeoutFactor(默認(rèn)是10)計(jì)算出時(shí)間值blackOutSeconds,該值不能大于上限connectionFailureCircuitTimeout(默認(rèn)30s)
  • 也就是說保證了斷路器最長(zhǎng)不能打開超過30s
  • 此方法不僅判斷了斷路器的打開與否,若打開順便打開斷路器應(yīng)該打開多長(zhǎng)時(shí)間(單位s)的方法,有了這個(gè)方法的理論做支撐,判斷當(dāng)前斷路器是否開啟就非常簡(jiǎn)單了:

    ServerStats:public boolean isCircuitBreakerTripped() {return isCircuitBreakerTripped(System.currentTimeMillis());}public boolean isCircuitBreakerTripped(long currentTime) {long circuitBreakerTimeout = getCircuitBreakerTimeout();if (circuitBreakerTimeout <= 0) {return false;}return circuitBreakerTimeout > currentTime;}

    當(dāng)觸發(fā)了熔斷器(連續(xù)失敗次數(shù)過多),斷路器開啟的時(shí)間范圍是:

    • 最大值:1<<16 * 10 = 320s
    • 最小值:1<<1 * 10 =100s

    當(dāng)然這值是根據(jù)配置走的,并且還有最大時(shí)間30s的限制哦~

    在Server被熔斷期間,負(fù)載均衡器都將忽略此Server


    斷路器如何閉合?

    倘若斷路器打開了,它如何恢復(fù)呢?有如下3種情形它會(huì)恢復(fù)到正常狀態(tài):

  • 不是連續(xù)失敗了,也就是成功了一次,那么successiveConnectionFailureCount就會(huì)立馬歸0,所以熔斷器就閉合了
  • 即使請(qǐng)求失敗了,但是并非是斷路器類異常,即不是RetryHandler#isCircuitTrippingException這種類型的異常時(shí)(比如RuntimeException就不是這種類型的異常),那就也不算連續(xù)失敗,所以也就閉合了
  • 到時(shí)間了,斷路器自然就自動(dòng)閉合了

  • 該斷路器和Hystrix的斷路器有何區(qū)別?

    很明顯,該斷路器規(guī)則非常簡(jiǎn)單,開啟與否完全由連續(xù)失敗來決定,而是否算失敗由RetryHandler#isCircuitTrippingException來決定,默認(rèn)它只認(rèn)為SocketException/SocketTimeoutException(或者其子類異常)屬于該種類型的異常哦~

    所以:你的程序在執(zhí)行時(shí)的任何業(yè)務(wù)異常(如NPE)和此斷路器沒有半毛錢關(guān)系

    當(dāng)然它們最大最大的區(qū)別是斷的對(duì)象不一樣:

    • 本斷路器斷的是Server,也就是遠(yuǎn)程服務(wù)器
    • Hystrix斷路器斷的是Client,也就是客戶端的調(diào)用

    當(dāng)然,關(guān)于Hystrix斷路器的內(nèi)容詳解請(qǐng)參考:[享學(xué)Netflix] 二十七、Hystrix何為斷路器的半開狀態(tài)?HystrixCircuitBreaker詳解


    獲取響應(yīng)時(shí)間邏輯

    一個(gè)Server服務(wù)器的響應(yīng)是最重要的衡量指標(biāo),因此它提供了大量的獲取響應(yīng)時(shí)間的方法:

    ServerStats:// 重要。獲取累計(jì)的,累計(jì)的,平均響應(yīng)時(shí)間// responseTimeDist里獲得的均是所有請(qǐng)求累計(jì)的public double getResponseTimeAvg() {return responseTimeDist.getMean();}public double getResponseTimeMax() {return responseTimeDist.getMaximum();}...// 樣本大小(每次獲取的值可能不一樣的哦,因?yàn)閐ataDist是時(shí)間窗口嘛)public int getResponseTimePercentileNumValues() {return dataDist.getSampleSize();}// 這段時(shí)間窗口內(nèi)(1分鐘)的平均響應(yīng)時(shí)間public double getResponseTimeAvgRecent() {return dataDist.getMean();}// ========下面是各個(gè)分位數(shù)的值======public double getResponseTime10thPercentile() {return getResponseTimePercentile(Percent.TEN);}...public double getResponseTime99point5thPercentile() {return getResponseTimePercentile(Percent.NINETY_NINE_POINT_FIVE);}

    狀態(tài)/指標(biāo)信息使用場(chǎng)景舉例

    統(tǒng)計(jì)信息都是非常有用的,這里先簡(jiǎn)單介紹,過個(gè)眼癮即可。它的使用均在負(fù)載均衡策略上,舉例:

    • WeightedResponseTimeRule:使用指標(biāo)ServerStats.responseTimeDist,獲取該Server的平均響應(yīng)時(shí)間來決策
    • AvailabilityFilteringRule:它用到了兩個(gè)指標(biāo)信息
      • 通過ServerStats.isCircuitBreakerTripped()判斷當(dāng)前斷路器是否打開作為該Server是否可用的判斷
      • ServerStats.activeRequestsCount找個(gè)活躍請(qǐng)求數(shù)最小的Server
    • ZoneAvoidanceRule:使用到了ServerStats.upServerListZoneMap和LoadBalancerStats.getZoneSnapshot

    默認(rèn)值不合理

    private static final int DEFAULT_PUBLISH_INTERVAL = 60 * 1000; private static final int DEFAULT_BUFFER_SIZE = 60 * 1000;

    這兩個(gè)默認(rèn)值決定了樣本量,以及樣本時(shí)間窗口。按這么設(shè)置:每收集一次持續(xù)1分鐘(問題不大),但是樣本大小是60 * 1000這個(gè)太高了:單臺(tái)機(jī)器QPS1000持續(xù)1分鐘才能填滿此窗口,我相信絕大部分情況下都是這么高的QPS的,所以此默認(rèn)值并不合理

    但是,但是,但是:ServerStats的唯一創(chuàng)建地方是LoadBalancerStats里:

    protected ServerStats createServerStats(Server server) {ServerStats ss = new ServerStats(this);//configure custom settingsss.setBufferSize(1000);ss.setPublishInterval(1000); ss.initialize(server);return ss; }

    兩個(gè)值均為1000,說明:每秒鐘收集一次(這個(gè)頻率太高了吧),然后樣本1000表示這1s內(nèi)要有1000的請(qǐng)求打進(jìn)來能打滿(QPS1000,也特高了)。所以實(shí)際上的默認(rèn)值真的也很不合理,它們均只適合高并發(fā)場(chǎng)景。。。

    坑爹的是,這兩個(gè)值并沒有提供鉤子or外部化配置讓我們可以隨意更改,唯一的鉤子是它是個(gè)protected方法,你只能通過繼承 + 復(fù)寫才行,而實(shí)際上我們很小概率回去復(fù)寫它(它在BaseLoadBalancer里創(chuàng)建)。

    說明:若你想更好的監(jiān)控,使得負(fù)載均衡效果更好點(diǎn),那么作為架構(gòu)師的你可以考慮定制定制哦~


    代碼示例

    @Test public void fun4() throws InterruptedException {ServerStats serverStats = new ServerStats();// 緩沖區(qū)大小最大1000。 若QPS是200,5s能裝滿它 這個(gè)QPS已經(jīng)很高了serverStats.setBufferSize(1000);// 5秒收集一次數(shù)據(jù)serverStats.setPublishInterval(5000);// 請(qǐng)務(wù)必調(diào)用此初始化方法serverStats.initialize(new Server("YourBatman", 80));// 多個(gè)線程持續(xù)不斷的發(fā)送請(qǐng)求request(serverStats);// 監(jiān)控ServerStats狀態(tài)monitor(serverStats);// hold主線程TimeUnit.SECONDS.sleep(10000); }// 單獨(dú)線程模擬刷頁面,獲取監(jiān)控到的數(shù)據(jù) private void monitor(ServerStats serverStats) {new Thread(() -> {ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);executorService.scheduleWithFixedDelay(() -> {System.out.println("=======時(shí)間:" + serverStats.getResponseTimePercentileTime() + ",統(tǒng)計(jì)值如下=======");System.out.println("請(qǐng)求總數(shù)(持續(xù)累計(jì)):" + serverStats.getTotalRequestsCount());System.out.println("平均響應(yīng)時(shí)間:" + serverStats.getResponseTimeAvg());System.out.println("最小響應(yīng)時(shí)間:" + serverStats.getResponseTimeMin());System.out.println("最大響應(yīng)時(shí)間:" + serverStats.getResponseTimeMax());System.out.println("樣本大小(取樣本):" + serverStats.getResponseTimePercentileNumValues());System.out.println("樣本下的平均響應(yīng)時(shí)間:" + serverStats.getResponseTimeAvgRecent());System.out.println("樣本下的響應(yīng)時(shí)間中位數(shù):" + serverStats.getResponseTime50thPercentile());System.out.println("樣本下的響應(yīng)時(shí)間90分位數(shù):" + serverStats.getResponseTime90thPercentile());}, 5, 5, TimeUnit.SECONDS);}).start(); }// 模擬請(qǐng)求(開啟5個(gè)線程,每個(gè)線程都持續(xù)不斷的請(qǐng)求) private void request(ServerStats serverStats) {for (int i = 0; i < 5; i++) {new Thread(() -> {while (true) {// 請(qǐng)求之前 記錄活躍請(qǐng)求數(shù)serverStats.incrementActiveRequestsCount();serverStats.incrementNumRequests();long rt = doSomething();// 請(qǐng)求結(jié)束, 記錄響應(yīng)耗時(shí)serverStats.noteResponseTime(rt);serverStats.decrementActiveRequestsCount();}}).start();} }// 模擬請(qǐng)求耗時(shí),返回耗時(shí)時(shí)間 private long doSomething() {try {int rt = randomValue(10, 200);TimeUnit.MILLISECONDS.sleep(rt);return rt;} catch (InterruptedException e) {e.printStackTrace();return 0L;} }// 本地使用隨機(jī)數(shù)模擬數(shù)據(jù)收集 private int randomValue(int min, int max) {return min + (int) (Math.random() * ((max - min) + 1)); }

    運(yùn)行程序,控制臺(tái)打印:

    =======時(shí)間:Tue Mar 17 21:27:49 CST 2020,統(tǒng)計(jì)值如下======= 請(qǐng)求總數(shù)(持續(xù)累計(jì)):240 平均響應(yīng)時(shí)間:103.43404255319149 最小響應(yīng)時(shí)間:10.0 最大響應(yīng)時(shí)間:199.0 樣本大小(取樣本):225 樣本下的平均響應(yīng)時(shí)間:102.38666666666667 樣本下的響應(yīng)時(shí)間中位數(shù):105.0 樣本下的響應(yīng)時(shí)間90分位數(shù):178.5 =======時(shí)間:Tue Mar 17 21:27:54 CST 2020,統(tǒng)計(jì)值如下======= 請(qǐng)求總數(shù)(持續(xù)累計(jì)):465 平均響應(yīng)時(shí)間:106.75869565217391 最小響應(yīng)時(shí)間:10.0 最大響應(yīng)時(shí)間:199.0 樣本大小(取樣本):225 樣本下的平均響應(yīng)時(shí)間:110.59555555555555 樣本下的響應(yīng)時(shí)間中位數(shù):115.5 樣本下的響應(yīng)時(shí)間90分位數(shù):185.0 =======時(shí)間:Tue Mar 17 21:27:59 CST 2020,統(tǒng)計(jì)值如下======= 請(qǐng)求總數(shù)(持續(xù)累計(jì)):701 平均響應(yīng)時(shí)間:106.35488505747126 最小響應(yīng)時(shí)間:10.0 最大響應(yīng)時(shí)間:200.0 樣本大小(取樣本):235 樣本下的平均響應(yīng)時(shí)間:105.39574468085107 樣本下的響應(yīng)時(shí)間中位數(shù):105.0 樣本下的響應(yīng)時(shí)間90分位數(shù):179.0 =======時(shí)間:Tue Mar 17 21:28:04 CST 2020,統(tǒng)計(jì)值如下======= 請(qǐng)求總數(shù)(持續(xù)累計(jì)):939 平均響應(yīng)時(shí)間:105.98929336188436 最小響應(yīng)時(shí)間:10.0 最大響應(yīng)時(shí)間:200.0 樣本大小(取樣本):240 樣本下的平均響應(yīng)時(shí)間:104.45 樣本下的響應(yīng)時(shí)間中位數(shù):104.0 樣本下的響應(yīng)時(shí)間90分位數(shù):181.0 =======時(shí)間:Tue Mar 17 21:28:09 CST 2020,統(tǒng)計(jì)值如下======= 請(qǐng)求總數(shù)(持續(xù)累計(jì)):1187 平均響應(yīng)時(shí)間:104.72673434856176 最小響應(yīng)時(shí)間:10.0 最大響應(yīng)時(shí)間:200.0 樣本大小(取樣本):246 樣本下的平均響應(yīng)時(shí)間:101.32926829268293 樣本下的響應(yīng)時(shí)間中位數(shù):103.0 樣本下的響應(yīng)時(shí)間90分位數(shù):177.0

    稍微核對(duì)一下數(shù)據(jù):

    • 平均rt大概100ms,所以1s鐘可以收到10次請(qǐng)求,5s的窗口就是收到50次請(qǐng)求
    • 公開啟5個(gè)線程,所以每個(gè)窗口內(nèi)收到的請(qǐng)求是50 * 5 = 250個(gè)左右
    • 觀察每次樣本大小數(shù):250左右

    可以看到數(shù)值都是吻合的,證明我們的示例木有啥問題。從控制臺(tái)看到Server的歷史持續(xù)狀態(tài)、抽樣的狀態(tài)值一覽無余,這就是監(jiān)控,這就是負(fù)載均衡的“糧食”。


    總結(jié)

    關(guān)于Ribbon對(duì)服務(wù)器狀態(tài)的管理ServerStats的介紹就到這了。本文花大篇幅介紹了很少人關(guān)注的Server狀態(tài)統(tǒng)計(jì)這塊的知識(shí)點(diǎn),是因?yàn)檫@對(duì)理解Ribbon的核心非常之重要,對(duì)Ribbon是如何負(fù)載均衡選擇Server的策略研究更是非常關(guān)鍵。

    建議小伙伴可以不僅局限于當(dāng)個(gè)“配置工程師”,而是花時(shí)間花精力深入其內(nèi)了解起來,內(nèi)部才是星辰大海,才有財(cái)富寶石。

    總結(jié)

    以上是生活随笔為你收集整理的Ribbon服务器状态:ServerStats及其断路器原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。