[享學Netflix] 二十五、Netflix Hystrix累計統計流、分發流、最大併發流、配置流、功能流(附代碼示例)

讓人迷茫的原因只有一個:你本該拼搏的年紀,卻想得太多,做得太少。

–> 返回專欄總目錄 <–
代碼下載地址:https://github.com/f641385712/netflix-learning

前言

上篇文章 介紹了Hystrix的“主流”:在滑動窗口內統計流、健康流。既然Hystrix的指標數據收集是基於事件驅動,那麼自然可以多一些監聽流,那麼本文將做個收尾,對Hystrix內置的累計統計流、分發流、最大併發流…等等分別做介紹,讓小夥伴們能對這種模式有個更深的理解,後面介紹的Hystrix各維度的監控都基於它們擴展出來的哦。

Hystrix已經內置了對事件監聽時各種流的實現,大多數數情況下無需自己來擴展實現的,當然若你着實要和第三方監控平臺深度集成,那麼你也可以自定義收集方式。


正文

累計統計流 BucketedCumulativeCounterStream

它和BucketedRollingCounterStream的區別是:它在減桶的過程中,持續/無限累積計數

public abstract class BucketedCumulativeCounterStream<Event extends HystrixEvent, Bucket, Output> extends BucketedCounterStream<Event, Bucket, Output> {

    private Observable<Output> sourceStream;
    private final AtomicBoolean isSourceCurrentlySubscribed = new AtomicBoolean(false);


    protected BucketedCumulativeCounterStream(HystrixEventStream<Event> stream, int numBuckets, int bucketSizeInMs,
                                              Func2<Bucket, Event, Bucket> reduceCommandCompletion,
                                              Func2<Output, Bucket, Output> reduceBucket) {
        super(stream, numBuckets, bucketSizeInMs, reduceCommandCompletion);

        this.sourceStream = bucketedStream
                .scan(getEmptyOutputValue(), reduceBucket) // 這是最大的區別,使用scan 一直掃描
                .skip(numBuckets)
                .doOnSubscribe(() -> isSourceCurrentlySubscribed.set(true))
                .doOnUnsubscribe(() -> isSourceCurrentlySubscribed.set(false))
                .share() 
                .onBackpressureDrop(); // 背壓:多餘的直接棄掉
    }

	// 實現父類方法
    @Override
    public Observable<Output> observe() {
        return sourceStream;
    }
}

最大的區別就是對bucketedStream的處理上,滑動窗口使用的是window + flatMap,而本處使用的是scan,代表着持續/無限累積計數。它有如下實現類:

在這裏插入圖片描述


CumulativeCommandEventCounterStream

CumulativeThreadPoolEventCounterStream

CumulativeCollapserEventCounterStream

以上實現類源碼此處均不展示,是因爲完全和BucketedRollingCounterStream體系的實現一模一樣,請參照上篇文章即可。唯一的不同在父類:一個只統計指定窗口,一個持續不斷的累計統計。


淺談metrics指標釋意

監控是大型分佈式系統的必備系統,它們的數據均來自一些指標信息。收集指標信息的庫有很多,其中比較出名的有metrics-core,它可以把收集到的信息提供給Meter、Histogram、Gauge...等度量工具使用,從而可以畫出如下美圖:

在這裏插入圖片描述

當然本文並不講述metrics-core如何用,而是以一段指標值爲例,稍加解釋:

{
    "version":"3.0.0",
    "timers":{
        "count":0,
        "max":0,
        "mean":0,
        "min":0,
        "p50":0,
        "p75":0,
        "p95":0,
        "p98":0,
        "p99":0,
        "p999":0,
        "stddev":0,
        "m15_rate":0,
        "m1_rate":0,
        "m5_rate":0,
        "mean_rate":0,
        "duration_units":"seconds",
        "rate_units":"calls/second"
    }
}

這個指標還是比較詳細的,裏面會有mean、p75、p99… 這個其實是很關鍵的數據指標。這些指標主要用於給你設置超時時間提供極有力的參考,如果你每每設置超時時間參考的是RT值是mean平均值,那你和瞎蒙沒啥區別。

另外到底參考那個值,要看你的系統的整體量級,以及需要滿足幾個9,比如要滿足三個9,那麼超時時間是需要謹慎的。


分位數p50、p95、p999代表什麼意思?

count/max/min/mean這些都不用解釋,其它主要關心:

  • p50:也叫中位數(注意中位數不是平均數)。它表示把數據總數分爲上下兩等分,中間的那個數值
  • 分位數:分位數是將總體的全部數據按從小到大順序排列後,處於各等分位置的變量值。
    • p95 = 10ms:代表95%的響應時間不大於10ms
    • p99、p999:含義同上

p表示:percent 百分比。

  • m15_rate:15分鐘內。請求數/每秒的比率
  • m1_rate:1分鐘內…
  • mean_rate: 平均每秒請求數(平均QPS,意義不大)
  • rate_units:calls/second” 比率單位,這裏表示每秒鐘請求數

對於監控指標來說,一般來說平均值幾乎沒有意義,而分位數一般是重點關注的值。


分佈流 RollingDistributionStream

在指定時間窗口內分佈流。說到分佈,所以和統計、畫圖有關。。。

public class RollingDistributionStream<Event extends HystrixEvent> { 
	... 
	// 訂閱者可以訂閱消費消息,得到各種分位數,都存在CachedValuesHistogram裏呢
    public Observable<CachedValuesHistogram> observe() {
        return rollingDistributionStream;
    }
}

雖然它不是抽象類,但它也沒標明具體監聽哪種事件,使用什麼數據流HystrixEventStream。總之最終它會監聽一個消息流(比如HystrixCommandStartStream它吧),然後通過RxJava的window操作符對一段時間內的數值進行運算操作,生成統計值放在Histogram對象中,然後重新發射

它內部集成使用度量工具org.HdrHistogram.Histogram來統計分析指標數據,並且給出非常詳細的分位數數據(最高達四個9 -> p9999)。它還有如下子類:

在這裏插入圖片描述


RollingCommandUserLatencyDistributionStream

LatencyDistribution:延遲發佈。延遲的值來自於:調用方線程提交請求和響應可見之間的時間間隔executionResult.getUserThreadLatency()


RollingCommandLatencyDistributionStream

延遲發佈。和上的區別是延遲時間來自於:executionResult.getExecutionLatency()表示:time spent in run() method

它們倆監聽的均是HystrixCommandCompletionStream數據流~


RollingCollapserBatchSizeDistributionStream

監聽了HystrixCollapserEventStream消息流,並且監聽窗口期內ADDED_TO_BATCH消息類型次數,通過Histogram計算後再發射出去。


最大併發流 RollingConcurrencyStream

它用於對最大併發進行統計:對一段時間內的執行併發量取最大值,如Command/ThreadPool的最大併發數。

// 竟然泛型都木有,乾淨利落
public abstract class RollingConcurrencyStream {
}

它監聽的是HystrixCommandExecutionStarted事件,它會發送併發數過來,從而便可獲得event.getCurrentConcurrency()對比每個桶(一個桶代表1s),最後取出最大值Math.max(a, b)


RollingCommandMaxConcurrencyStream

RollingThreadPoolMaxConcurrencyStream

因爲監聽HystrixCommandExecutionStarted事件的有兩種事件流:command的和ThreadPool的,所以必須用兩個類來表示。它倆除了關心的事件不一樣,其它都一樣~


配置流 HystrixConfigurationStream

這個類對當前的Hystrix配置進行採樣,並將其作爲流公開。

public class HystrixConfigurationStream {

	...
	private final Observable<HystrixConfiguration> allConfigurationStream;
	...
    public Observable<HystrixConfiguration> observe() {
        return allConfigurationStream;
    }
    
    // 當然還可以當讀監控某一類配置
    public Observable<Map<HystrixCommandKey, HystrixCommandConfiguration>> observeCommandConfiguration() {
        return allConfigurationStream.map(getOnlyCommandConfig);
    }
    public Observable<Map<HystrixThreadPoolKey, HystrixThreadPoolConfiguration>> observeThreadPoolConfiguration() {
        return allConfigurationStream.map(getOnlyThreadPoolConfig);
    }
    public Observable<Map<HystrixCollapserKey, HystrixCollapserConfiguration>> observeCollapserConfiguration() {
        return allConfigurationStream.map(getOnlyCollapserConfig);
    }
    ...
}

可使用hystrix.stream.config.intervalInMilliseconds = 5000來配置多長時間採樣一次,默認5000ms也就是5秒採樣一次。另外com.netflix.hystrix.contrib.sample.stream.HystrixConfigSseServlet就是用該流來獲取配置信息的。


功能流 HystrixUtilizationStream

Utilization:使用、利用(使用率、利用率)。這個類對當前Hystrix資源的利用情況進行採樣,並將其公開爲流。

public class HystrixUtilizationStream {

	// HystrixUtilization就是最終的數據結構格式,下面給使用示例
	private final Observable<HystrixUtilization> allUtilizationStream;
	...
    public Observable<HystrixUtilization> observe() {
        return allUtilizationStream;
    }

    public Observable<Map<HystrixCommandKey, HystrixCommandUtilization>> observeCommandUtilization() {
        return allUtilizationStream.map(getOnlyCommandUtilization);
    }
    public Observable<Map<HystrixThreadPoolKey, HystrixThreadPoolUtilization>> observeThreadPoolUtilization() {
        return allUtilizationStream.map(getOnlyThreadPoolUtilization);
    }
}

可使用hystrix.stream.utilization.intervalInMilliseconds = 500來配置多長時間採樣一次,默認500ms採樣一次。另外com.netflix.hystrix.contrib.sample.stream.HystrixUtilizationSseServlet就是用該流來獲取資源利用信息的。


使用示例

public class CommandHelloWorld extends HystrixCommand<String> {
    private final String name;

    // 指定一個HystrixCommandGroupKey,這樣熔斷策略會按照此組執行
    public CommandHelloWorld(String name) {
        super(HystrixCommandGroupKey.Factory.asKey("MyAppGroup"));
        this.name = name;
    }

    @Override
    protected String run() {
        if(name == null){
            throw new NullPointerException();
        }
        return "Hello " + name + "!";
    }

    @Override
    protected String getFallback() {
        // super.getFallback():No fallback available.
        return "this is fallback msg";
    }
}
private static final String toJsonString(Object obj) {
    ObjectMapper mapper = new ObjectMapper();
    try {
        return mapper.writeValueAsString(obj);
    } catch (JsonProcessingException e) {
        throw new RuntimeException(e);
    }
}


@Test
public void fun1() throws InterruptedException {
    // 查看command、線程池的使用情況
    HystrixUtilizationStream utilizationStream = HystrixUtilizationStream.getInstance();
    // utilizationStream.observeThreadPoolUtilization()
    utilizationStream.observe().subscribe(d -> System.out.println(toJsonString(d)));

    // 查看配置情況
    HystrixConfigurationStream configStream = HystrixConfigurationStream.getInstance();
    configStream.observe().subscribe(d -> {
        System.out.println(d);
    });

    // 累計統計流
    HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey("CommandHelloWorld");
    HystrixPropertiesCommandDefault properties = new HystrixPropertiesCommandDefault(commandKey, HystrixCommandProperties.Setter());
    CumulativeCommandEventCounterStream counterStream = CumulativeCommandEventCounterStream.getInstance(commandKey, properties);
    counterStream.observe().subscribe(d -> System.out.println(toJsonString(d)));

    // 最大併發流
    RollingCommandMaxConcurrencyStream concurrencyStream = RollingCommandMaxConcurrencyStream.getInstance(commandKey, properties);
    concurrencyStream.observe().subscribe(d -> System.out.println(toJsonString(d)));

    // 發送事件(發送多次)
    CommandHelloWorld helloWorld = new CommandHelloWorld("YoutBatman");
    helloWorld.execute();

    helloWorld = new CommandHelloWorld("YoutBatman");
    helloWorld.queue();

    // 走fallabck
    helloWorld = new CommandHelloWorld(null);
    helloWorld.queue();


    // 因爲配置5秒鐘才能打印一次
    TimeUnit.SECONDS.sleep(5);

}

運行程序,控制檯輸出:

資源利用情況:

{
    "commandUtilizationMap":{
        "CommandHelloWorld":{
            "concurrentCommandCount":0
        }
    },
    "threadPoolUtilizationMap":{
        "MyAppGroup":{
            "currentActiveCount":0,
            "currentCorePoolSize":10,
            "currentPoolSize":3,
            "currentQueueSize":0
        }
    }
}

這是功能流的數據,最明顯的是啓動了三次任務,線程池大小目前是3。另外,因爲配置流中的對應無法很好的用JSON序列化,這裏我只能採用笨拙的截圖的方式展示嘍(下面配置不生效哦,若配置了信號量,那麼ThreadPoolConfig這一欄就爲null了):

# hystrix.command.default.execution.isolation.strategy = SEMAPHORE
# hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests = 2

在這裏插入圖片描述
在這裏插入圖片描述
累計統計流的數據如下:

[0,2,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0]

對數字解釋如下:

  • 2:index=1,對應事件爲HystrixEventType.SUCCESS
  • 1:index=2,對應事件爲HystrixEventType.FAILURE
  • 1:index=9,對應事件爲HystrixEventType.FALLBACK_SUCCESS

如果你願意,你還可以自行模擬出TIMEOUT超時、FALLBACK_FAILURE回滾失敗等等情況,建議親可試試,以加深理解。

最大併發流輸出的數據是1,因爲很明顯1秒內最多才一個請求嘛~

HealthCountsStream健康信息彙總:

{"errorCount":1,"errorPercentage":33,"totalRequests":3}

一共3個請求,失敗了一個,所以錯誤率是33%

說明:因爲HealthCountsStream它默認是500ms照一次快照,所以此處它會打印10次(共5s嘛)


總結

到此,關於Netflix Hystrix指標收集,以及轉換爲Stream流式的實現已經全部講述完成了。最後用一張大佬手繪圖對此作出總結:

在這裏插入圖片描述

分隔線

聲明

原創不易,碼字不易,多謝你的點贊、收藏、關注。把本文分享到你的朋友圈是被允許的,但拒絕抄襲。你也可【左邊掃碼/或加wx:fsx641385712】邀請你加入我的 Java高工、架構師 系列羣大家庭學習和交流。
往期精選

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章