天天看點

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

作者:程式員進階碼農II

責任鍊模式在Sentinel中的應用

在前面,我們将Sentinel提供的所有ProcessorSlot分為兩類:一類是負責完成資源名額資料統計的ProcessorSlot;一類是實作限流、熔斷等流量控制功能的ProcessorSlot。

Sentinel使用責任鍊模式将注冊的所有ProcessorSlot按照一定的順序串成一個單向連結清單。

實作資源名額資料統計的ProcessorSlot必須在實作流量控制功能的ProcessorSlot的前面,原因很簡單,限流、熔斷降級等都需要依賴資源的實時名額資料做判斷。當然,如果ProcessorSlot不依賴資源的名額資料,那麼這個ProcessorSlot的位置就不需要限制。

除按分類排序外,同一個分類下的每個ProcessorSlot可能也需要有嚴格的排序。例如,負責完成資源名額資料統計的ProcessorSlot的排序為NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot,如果排序亂了,就會抛出異常。

ProcessorSlotChain用于将ProcessorSlot串成一個單向連結清單,并且ProcessorSlotChain由SlotChainBuilder構造。DefaultSlotChainBuilder是預設使用的SlotChainBuilder,其構造的ProcessorSlotChain所注冊的ProcessorSlot及順序如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

ProcessorSlot接口的定義如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

方法說明如下。

• entry:入口方法。

• fireEntry:調用下一個ProcessorSlot的entry方法。

• exit:出口方法。

• fireExit:調用下一個ProcessorSlot的exit方法。

參數說明如下。

• context:目前調用鍊上下文,即Context執行個體。

• resourceWrapper:資源ID。

• param:泛型參數,一般用于傳遞資源的DefaultNode執行個體。

• count:表示申請占用共享資源的數量,隻有申請到足夠的共享資源時才能繼續執行。

• prioritized:表示是否對請求進行優先級排序。

• args:調用方法傳遞的參數,用于實作熱點參數限流。

Sentinel将需要被保護的資源包裝起來,這與鎖的實作是一樣的,即需要先擷取鎖才能繼續執行。count與并發程式設計AQS中tryAcquire方法的參數作用一樣,例如,線程池有200個線程,目前方法執行需要申請3個線程才能執行,那麼count就是3。

count的值一般為1,當限流規則配置的限流門檻值類型為Threads時,表示需要申請一個線程,當限流規則配置的限流門檻值類型為QPS時,表示需要申請令牌(假設使用令牌桶算法)。

之是以能夠将所有的ProcessorSlot構造成一個ProcessorSlotChain,是因為這些ProcessorSlot繼承了AbstractLinkedProcessorSlot類。AbstractLinkedProcessorSlot類有一個指向下一個ProcessorSlot的字段,正是這個字段實作了将所有注冊的ProcessorSlot串成一條單向連結清單。AbstractLinkedProcessorSlot類的部分源碼如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

• next:表示連結清單中目前節點的下一個節點。

實作責任鍊調用的方法:由前一個AbstractLinkedProcessorSlot執行個體調用fireEntry方法或fireExit方法,在fireEntry方法與fireExit方法中調用下一個AbstractLinkedProcessorSlot執行個體(next)的entry方法或exit方法。AbstractLinkedProcessorSlot執行個體的fireEntry方法與fireExit方法的實作源碼如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

• fireEntry方法:如果next不為空,則調用下一個ProcessorSlot的entry方法。

• fireExit方法:如果next不為空,則調用下一個ProcessorSlot的exit方法。

ProcessorSlotChain抽象類也繼承了AbstractLinkedProcessorSlot類,隻不過添加了兩個方法:将一個ProcessorSlot添加到連結清單頭節點的addFirst方法,以及将一個ProcessorSlot添加到連結清單末尾的addLast方法。

ProcessorSlotChain抽象類的預設實作子類是DefaultProcessorSlotChain,DefaultProcessorSlotChain類定義了一個指向連結清單頭節點的first字段和一個指向連結清單尾節點的end字段。其中,first字段指向的是一個空實作的AbstractLinkedProcessorSlot執行個體。

DefaultProcessorSlotChain類的源碼如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

• first字段:指向連結清單的頭節點。

• end字段:指向連結清單的尾節點。

• addFirst方法:在連結清單頭部添加一個節點。

• addLast方法:在連結清單尾部添加一個節點。

• entry方法:調用連結清單頭節點的entry方法,由頭節點調用下一節點的entry方法。

• exit方法:調用連結清單頭節點的exit方法,由頭節點調用下一節點的exit方法。

責任鍊模式是非常常用的一種設計模式。在Shiro架構中,使用責任鍊模式實作資源通路權限過濾的骨架(過濾器鍊);在Netty架構中,使用責任鍊模式将處理請求的ChannelHandler包裝為連結清單,實作局部串行處理請求。

在責任鍊的實作上,Sentinel與Netty有相似的地方。在Sentinel中,ProcessorSlot執行個體的entry方法是按ProcessorSlot執行個體在連結清單中的順序被調用的,這一點與Netty中ChannelHandler連結清單的實作相同,不同的是,ProcessorSlot執行個體的exit方法被調用的順序是從後往前的。

Netty可以實作局部串行以解決線程安全問題,即ChannelHandler連結清單隻在收到資料包時被建立,在響應資料包後被銷毀,而由于Sentinel是以資源為次元的,是以必然實作不了這種局部串行。

Sentinel會為每個資源建立且僅建立一個ProcessorSlotChain執行個體。ProcessorSlotChain執行個體被緩存在CtSph類的chainMap靜态字段中,key為資源ID,每個資源的ProcessorSlotChain執行個體在CtSph#entryWithPriority方法中被建立,代碼如下。

2000字搞懂深度解析微服務高并發:責任鍊模式在Sentinel中的應用

• chainMap字段:緩存ProcessorSlotChain執行個體,key為資源ID,value為ProcessorSlotChain執行個體。

• entryWithPriority方法:為資源建立ProcessorSlotChain執行個體(如果未建立)、為資源建立Entry執行個體,以及調用ProcessorSlotChain執行個體的entry方法。

CtSph類的chainMap靜态字段最終會緩存整個應用中所有資源的ProcessorSlotChain執行個體。雖然chainMap的key類型為ResourceWrapper,但ResourceWrapper的equals方法隻比較資源的名稱,是以一個資源不會存在多個ProcessorSlotChain執行個體。

本文給大家講解的内容是深度解析微服務高并發了解整體工作流程:責任鍊模式在Sentinel中的應用

  1. 下篇文章給大家講解的内容是深度解析微服務高并發了解整體工作流程:Sentinel的整體工作流程分析
  2. 感謝大家的支援!

繼續閱讀