Sentinel原理一覽

Real_man發表於2020-04-06

在分散式系統中,由於服務數量增多,出現問題的可能性也會增大。想象一下,如果我們上游的系統突然流量增大N倍,超出我們系統承載的流量瞬間進來會不會壓垮我們的系統? 同樣對於我們下游的應用,此刻不知道什麼原因導致RT特別的長,那麼我們系統提供的服務是不是也會收到影響呢?

image-20200406140842159

其它系統都是出於不可控的狀態,想要要求其它系統穩定執行的前提是要保證你自己能穩定執行。因此我們要做的就是不管其它系統如何,我們都穩定執行。那麼如何做呢?市場上經典的工具如Hystrix,當然阿里也提供了一套更加強大和易用的工具,Sentinel。

Sentinel,Hystrix對比:github.com/alibaba/Sen…

結論:因為我們需要保護自己的系統,在流量過大或者下游響應時間過長以及其他意外情況的時候,我們的系統還需要正常執行,雖然市場上有一些其它的工具,但是Sentinel功能更強大,用起來更簡單,擴充套件起來也方便

Sentinel主要功能:

  • 流量控制:將請求調整成合適的形狀。
  • 熔斷降級:當依賴的某個資源不穩定的時候,對呼叫的資源進行限制,讓請求快速失敗。
  • 系統負載保護:如CPU過高,執行緒池數量太大,則需要對新來的請求做一些限制。

參考:Sentinel是什麼

基本用法

雖然Sentinel提供了很多種使用的方式,但是大部分時候基本的用法就已經滿足我們的要求了。這裡只說下常見的用法。

1 引入依賴,寫Sentinel程式碼

			  Entry sentinel = null;
        try {
            sentinel = SphU.entry(request.getName());
            // 業務程式碼
        } 				
				catch (BlockException blockException) {
            log.error("BlockException! request={}", request, blockException);         
        }           
				finally {
            if (sentinel != null){
                sentinel.exit();
            }
        }    
複製程式碼

2 以上只是代表我們有了限流的工具,但是沒有定義具體的規則。通常在dashboard配置規則,不過再程式碼中預定義一些預設規則也可以。

引入Dashboard:github.com/alibaba/Sen…

Sentinel支援的規則有以下幾種:

  • 流量控制規則:對應程式碼中的FlowRule
  • 熔斷降級規則:對應DegradeRule
  • 系統保護規則:對應SystemRule
  • 來源訪問控制規則:對應AuthorityRule
  • 熱點引數規則:對應ParamFlowRule

每種rule都有自己對應的RuleManager使其生效,比如在程式碼中預定義流量控制規則:

   // 定義流量控制規則,限制QPS不高於20
	 private static void initFlowRules() {
        List<FlowRule> rules = new ArrayList<>();
        FlowRule rule = new FlowRule();
        rule.setResource("HelloWorld");
        // set limit qps to 20
        rule.setCount(20);
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rules.add(rule);

        // 將規則載入到記憶體,後續流量控制根據載入的規則進行處理
        FlowRuleManager.loadRules(rules);
    }
複製程式碼

3 檢視dashboard,或者日誌觀察限流降級是否生效。

  • 日誌位置:/家目錄/logs/csp/sentinel相關日誌
  • 監控皮膚:可以檢視各種規則當前的狀態
    img

使用方式上,自己摸索下就知道怎麼回事了。就不說太多了。

原理概覽

以以下程式碼為例,Sentinel內部做了什麼操作,來實現限流降級功能呢?

SphU.entry("HelloWorld")
複製程式碼

1 Sentinel針對資源進行操作,entry("HelloWorld")代表要處理名稱為HelloWorld的資源。操作成功後會返回一個Entry物件,否則丟擲異常代表不處理當前請求(可以認為是規則限制)

2 Sphu.entry內部關鍵部分程式碼。

// 建立當前呼叫的上下文資訊,為ThreadLocal變數。
// 如果是我們手工建立Context,一般可以指定name與origin(來源)。
// Context內部主要儲存:
// - entranceNode  當前呼叫的入口Node
// - curEntry  當前正在處理的Entry,即每一次呼叫都會生成的Entry物件
// - origin 字串,用來標記來源,統計的時候會用到
Context context = createContext();

// 查詢當前資源的Slot處理鏈,一般一個資源對應一個ProcessorSlotChain,如果沒有找到ProcessorSlotChain會建立預設的DefaultSlotChainBuilder。其中SLot順序如下:
//       ProcessorSlotChain chain = new DefaultProcessorSlotChain();
//        chain.addLast(new NodeSelectorSlot());
//        chain.addLast(new ClusterBuilderSlot());
//        chain.addLast(new LogSlot());
//        chain.addLast(new StatisticSlot());
//        chain.addLast(new AuthoritySlot());
//        chain.addLast(new SystemSlot());
//        chain.addLast(new FlowSlot());
//        chain.addLast(new DegradeSlot());
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);


// 建立Entry與SlotChain處理Entry
// 其中依次呼叫上面的Slot進行處理。
Entry e = new CtEntry(resourceWrapper, chain, context);
chain.entry(context, resourceWrapper, null, count, prioritized, args);
複製程式碼

SlotChain處理對應官方的架構圖如下:

image.png

  • TreeNodeBuilder對應NodeSelectorSlot,主要負責收集資源的路徑,並將這些資源的呼叫路徑,以樹狀結構儲存起來,用於根據呼叫路徑來限流降級
  • ClusterNode對應ClusterBuilderSlot,用於儲存資源的統計資訊以及呼叫者資訊,例如該資源的 RT, QPS, thread count 等等,這些資訊將用作為多維度限流,降級的依據;
  • StatisticSlot:核心的Slot。各種維度的監控統計位於其中
  • FlowSlot,DegradeSlot,AuthoritySlot,SystemSlot:實現方式上基本一致,主要根據統計的資料判斷當前的規則是否生效,看懂了其中一個,其餘的可以舉一反三,當然也可以寫自己自定義的控制規則。

3 根據程式碼,我覺得當前一個比較完整的流程圖應該是:

image-20200406152106146

系統保護原理

上面說了大致流程,看一個具體的規則來了解它是如何生效的。

1 在Sentinel獲取資源的時候,會呼叫SlotChain,正常情況下會走到SystemSlot中進行處理。

2 SystemSlot,交給RuleManager檢查資源是否滿足條件。

SystemRuleManager.checkSystem(resourceWrapper);
複製程式碼

3 規則校驗,可以看到如果系統負載過高,則會丟擲SystemBlockException異常,終止當前的請求處理。

主要程式碼在SystemRuleManager中

				...
				
        // load. BBR algorithm.
        if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
            if (!checkBbr(currentThread)) {
                throw new SystemBlockException(resourceWrapper.getName(), "load");
            }
        }

        // cpu usage
        if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {
            throw new SystemBlockException(resourceWrapper.getName(), "cpu");
        }
複製程式碼

其它:

  • 系統狀態通過SystemStatusListener進行更新,排程器每秒鐘收集一次系統狀態
  • 通過SystemRuleManager.loadRules在載入規則
  • 系統規則配置實體類為:SystemRule

流控QPS直接拒絕

Sentinel提供了多種流控方式,直接拒絕Warm Up勻速排隊。對應 FlowRule 中的 controlBehavior 欄位。

直接拒絕實現比較簡單,通過看它的實現後續再弄明白更復雜的實現。與系統保護類似:

Request -> FlowSlot -> FlowRulechecker -> FlowRuleManager -> FlowRule -> ControlBehavior(TrafficShapingController) -> canPass

image-20200406154138713

小結

這裡主要是說了Sentinel的簡單使用,以及執行過程中程式碼的大體流程,並未深究其資料結構。

程式碼設計上並不算很複雜,如果在使用的過程中遇到什麼問題,點進去看一下大致也能解決。我覺得其核心的程式碼:

  • StatisticSlot:多種維度的資料統計入口,在其中使用了LeapArray統計秒級指標資料。
  • FlowSlot,DegradeSlot,AuthoritySlot,SystemSlot:熔斷,限流降級邏輯判斷的地方。如果想判斷規則有沒有生效,就到對應的類中Debug下。

更多內容參考:github.com/alibaba/Sen…

相關文章