02.29 Spring Cloud Alibaba:Sentinel實現熔斷與限流

一、什麼是Sentinel

Sentinel,中文翻譯為哨兵,是為微服務提供流量控制、熔斷降級的功能,它和Hystrix提供的功能一樣,可以有效的解決微服務調用產生的“雪崩效應”,為微服務系統提供了穩定性的解決方案。隨著Hystrix進入了維護期,不再提供新功能,Sentinel是一個不錯的替代方案。通常情況下,Hystrix採用線程池對服務的調用進行隔離,Sentinel採用了用戶線程對接口進行隔離,二者相比,Hystrix是服務級別的隔離,Sentinel提供了接口級別的隔離,Sentinel隔離級別更加精細,另外Sentinel直接使用用戶線程提供限制,相比Hystrix的線程池隔離,減少了線程切換的開銷。另外Sentinel的DashBoard提供了在線更改限流規則的配置,也更加的優化。

二、開源生態

Spring Cloud Alibaba:Sentinel實現熔斷與限流

Sentinel和Hystrix

Sentinel和Hystrix

Spring Cloud Alibaba:Sentinel實現熔斷與限流

三、Sentinel特性

Spring Cloud Alibaba:Sentinel實現熔斷與限流

1、豐富的應用場景

Sentinel承接了阿里巴巴近十年雙十一大促流量的核心場景,例如秒殺(突發流量控制在系統容量可以承受的範圍)、消息削峰填谷、實時熔斷下游不可用應用等。

2、完備的實時監控

Sentinel同時提供實時的監控功能。我們可以在控制檯中看到接入應用的單臺機器秒級數據,甚至500臺以下規模的集群的彙總進行情況。

3、廣泛的開源生態

Sentinel提供開箱即用的與其它開源框架的整合模塊,例如與spring cloud、dubbo、grpc的整合。我們只需要引入響應的依賴並進行簡單的配置即可快速的接入Sentinel。

4、完美的SPI擴展點

Sentinel提供簡單易用、完善的SPI擴展點。我們可以通過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。

四、資源和規則

資源是Sentinel的關鍵概念。它可以是java應用程序中的任何內容,例如,由應用程序提供的服務,或由應用程序調用的其它應用提供的服務,甚至可以是一段代碼。只是通過Sentinel API定義的代碼,就是資源,能夠被Sentinel保護起來,大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來表示資源。

圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。

Sentinel中調用SphU或者SphO的entry方法獲取限流資源,不同的是前者獲取限流資源失敗時會跑BlockException異常,後者返回false,二者的實現都是基於CtSph類完成的。

Spring Cloud Alibaba:Sentinel實現熔斷與限流

五、核心概念

1、Resource

resource是Sentinel中最重要的一個概念,Sentinel通過資源來保護具體的業務代碼或其它後方服務。Sentinel把複雜的邏輯給屏蔽了,用戶只需要為受保護的代碼或服務定義一個資源,然後定義規則就可以了,剩下的通通交給Sentinel來處理。並且資源和規則是解耦的,規則甚至可以在運行時動態修改。定義完資源後,就可以通過在程序中埋點來保護你自己的服務,埋點的方式有兩種:

(1)try-catch方式(通過SphU.entry(...)),當catch到BlockException時執行異常處理或fallback。

(2)if-else方式(通過SphO.entry(...)),當返回false時執行異常處理或fallback。

以上兩種方式都是通過硬編碼的形式定義資源然後進行資源埋點的,對業務代碼的侵入太大,從0.1.1版本開始,Sentinel加入了註解的支持,可以通過註解來定義資源,具體的註解為:SentinelResource。通過註解除了可以定義資源外,還可以指定blockHandler和fallback方法。

在Sentinel中具體表示資源的類:ResourceWrapper,它是一個抽象的包裝類,包裝了資源的Name和EntryType。他有兩個實現類,分別是:StringResourceWrapper和MethodResourceWrapper。顧名思義,StringResourceWrapper是通過對一串字符進行包裝,是一個通用的資源包裝類,MethodResourceWrapper是對方法調用的包裝。

2、Context

Context是對資源操作時的上下文環境,每個資源操作(針對resource的entry和exit)必須屬於一個Context,如果程序中未指定Context,會創建name為“Sentinel_default_context”的默認Context。一個Context生命週期內可能有多個資源操作,Context生命週期內的最後一個資源exit時會清理該Context,這也預示著整個Context生命週期的結束。Context主要屬性如下:

<code>public class Context {   // context名字,默認名字 "sentinel_default_context"   private final String name;   // context入口節點,每個context必須有一個entranceNode   private DefaultNode entranceNode;   // context當前entry,Context生命週期中可能有多個Entry,所有curEntry會有變化   private Entry curEntry;   // The origin of this context (usually indicate different invokers, e.g. service consumer name or origin IP).   private String origin = "";   private final boolean async;}/<code>

一個Context生命週期內Context只能初始化一次,存到ThreadLocal中,並且只有在非NULL時才會進行初始化。如果想在調用SphU.entry()或SphO.entry()前,自定義一個context,則通過ContextUtil.enter()方法來創建。context保存在ThreadLocal中,每次執行的時候會優先到ThreadLocal中獲取,為null時會創建一個context。當Entry執行exit方法時,如果entry的parent節點為null,表示當前context中最外層的entry了,此時將threadLocals中的context清空。

3、Entry

每次執行SphU.entry()或SphO.entry()都會返回一個Entry,Entry表示一次資源操作,內部會保存單籤invocation信息。在一個context聲明週期中多次資源操作,也就是對應多個Entry,parent/child結構保存在Entry實例中,Entry類CtEntry結構如下:

<code>class CtEntry extends Entry {   protected Entry parent = null;   protected Entry child = null;   protected ProcessorSlot<object> chain;   protected Context context;}public abstract class Entry implements AutoCloseable {   private long createTime;   private Node curNode;   /**    * {@link Node} of the specific origin, Usually the origin is the Service Consumer.    */   private Node originNode;   private Throwable error; // 是否出現異常   protected ResourceWrapper resourceWrapper; // 資源信息}/<object>/<code>
Spring Cloud Alibaba:Sentinel實現熔斷與限流

4、DefaultNode

Node默認實現類DefaultNode,該類還有一個子類EntranceNode;context有一個entranceNode屬性,Entry中有一個curNode屬性。

  • EntranceNode:該類的創建在初始化context時完成的,注意該類是針對context維度的,也就是一個context有且僅有一個EntranceNode。
  • DefaultNode:該類的創建是在NodeSelectorSlot.entry完成的,當不存在context.name對應的DefaultNode時會創建並保存在本地緩存;獲取到context.name對應的DefaultNode後將該DefaultNode設置到當前context的curEntry.curNode屬性,也就是說,在DefaultSelectorSlot中是一個context有且僅有一個DefaultNode。

看到這裡,你是不是有疑問?為什麼一個context有且僅有一個DefaultNode,我們的resouece跑哪去了呢,其實,這裡的一個context有且僅有一個DefaultNode是在NodeSelectorSlot範圍內,NodeSelectorSlot是ProcessorSlotChain中的一環,獲取ProcessorSlotChain是根據Resource維度來的。總結為一句話就是:針對同一個Resource,多個context對應多個DefaultNode;針對不同Resource,(不管是否是同一個context)對應多個不同DefaultNode。這還沒看明白 : ),好吧,我不bb了,上圖吧:

Spring Cloud Alibaba:Sentinel實現熔斷與限流

DefaultNode結構如下:

<code>public class DefaultNode extends StatisticNode {   private ResourceWrapper id;   /**    * The list of all child nodes.    * 子節點集合    */   private volatile Set<node> childList = new HashSet<>();   /**    * Associated cluster node.    */   private ClusterNode clusterNode;}/<node>/<code>

一個Resouce只有一個clusterNode,多個defaultNode對應一個clusterNode,如果defaultNode.clusterNode為null,則在ClusterBuilderSlot.entry中會進行初始化。 同一個Resource,對應同一個ProcessorSlotChain,這塊處理邏輯在lookProcessChain方法中,如下:

<code>ProcessorSlot<object> lookProcessChain(ResourceWrapper resourceWrapper) {   ProcessorSlotChain chain = chainMap.get(resourceWrapper);   if (chain == null) {       synchronized (LOCK) {           chain = chainMap.get(resourceWrapper);           if (chain == null) {               // Entry size limit.               if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {                   return null;              }               chain = SlotChainProvider.newSlotChain();               Map<resourcewrapper> newMap = newHashMap<resourcewrapper>(                   chainMap.size() + 1);               newMap.putAll(chainMap);               newMap.put(resourceWrapper, chain);               chainMap = newMap;          }      }  }   return chain;}/<resourcewrapper>/<resourcewrapper>/<object>/<code>

5、StatisticNode

StatisticNode中保存了資源的實時統計數據(基於滑動時間窗口機制),通過這些統計數據,sentinel才能進行限流、降級等一系列操作。StatisticNode屬性如下:

<code>public class StatisticNode implements Node {   /**    * 秒級的滑動時間窗口(時間窗口單位500ms)    */   private transient volatile Metric rollingCounterInSecond = newArrayMetric(SampleCountProperty.SAMPLE_COUNT,       IntervalProperty.INTERVAL);   /**    * 分鐘級的滑動時間窗口(時間窗口單位1s)    */   private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000,false);   /**    * The counter for thread count. * 線程個數用戶觸發線程數流控    */   private LongAdder curThreadNum = new LongAdder();}public class ArrayMetric implements Metric {   private final LeapArray<metricbucket> data;}public class MetricBucket {// 保存統計值   private final LongAdder[] counters;// 最小rt   private volatile long minRt;}/<metricbucket>/<code>

其中MetricBucket.counters數組大小為MetricEvent枚舉值的個數,每個枚舉對應一個統計項,比如PASS表示通過個數,限流可根據通過的個數和設置的限流規則配置count大小比較,得出是否觸發限流操作,所有枚舉值如下:

<code>public enum MetricEvent {   PASS, // Normal pass.   BLOCK, // Normal block.   EXCEPTION,   SUCCESS,   RT,   OCCUPIED_PASS}/<code>

6、Slot

Slot是sentinel中非常重要的概念,sentinel的工作流程就是圍繞著一個個插槽所組成的插槽鏈來展開的。需要注意的是每個插槽都有自己的職責,他們各司其職完美的配合,通過一定的編排順序,來達到最終的限流降級。默認的各個插槽之間的順序是固定的,因為有的插槽需要依賴其他的插槽計算出來的結果才能進行工作。

sentinel通過SlotChainBuilder作為SPI接口,使得Slot Chain具備了擴展的能力。我們可以通過實現SlotChainBuilder接口加入自定義Slot並且定義編排各個slot之間的排序,從而可以給sentinel添加自定義的功能。

那SlotChain是在哪創建的呢?是在 CtSph.lookProcessChain() 方法中創建的,並且該方法會根據當前請求的資源先去一個靜態的HashMap中獲取,如果獲取不到才會創建,創建後會保存到HashMap中。這就意味著,同一個資源會全局共享一個SlotChain。默認生成ProcessorSlotChain為:

<code>// DefaultSlotChainBuilderpublic ProcessorSlotChain build() {   ProcessorSlotChain chain = new DefaultProcessorSlotChain();   chain.addLast(new NodeSelectorSlot());   chain.addLast(new ClusterBuilderSlot());   chain.addLast(new LogSlot());   chain.addLast(new StatisticSlot());   chain.addLast(new SystemSlot());   chain.addLast(new AuthoritySlot());   chain.addLast(new FlowSlot());   chain.addLast(new DegradeSlot());   return chain;/<code>
Spring Cloud Alibaba:Sentinel實現熔斷與限流

六、springcloud如何使用sentinel

學習了sentinel核心概念之後,感覺整個人都不好了,真的是晦澀難懂,來個helloworld,輕鬆一下。

1、pom.xml

<code><dependency>    <groupid>org.springframework.cloud/<groupid>    <artifactid>spring-cloud-starter-alibaba-sentinel/<artifactid>/<dependency>/<code>

2、 controller

<code>@RestControllerpublic class TestController {    @GetMapping(value = "/hello")    @SentinelResource("hello")    public String hello() {        return "Hello Sentinel";    }}/<code>

3、引入dashboard

直接下載sentinel-dashboard的jar包。

默認是8080端口,在瀏覽器輸入:localhost:8080,默認賬號密碼:sentinel:sentinel,看到控制檯界面為部署成功。

Spring Cloud Alibaba:Sentinel實現熔斷與限流

4、application.properties

<code>server.port=8088spring.application.name=spring-cloud-alibaba-sentinel-demo# sentinel dashboardspring.cloud.sentinel.transport.dashboard=localhost:8080/<code>

5、 啟動spring boot 項目,繼續訪問localhost:8080,會看到如下界面

Spring Cloud Alibaba:Sentinel實現熔斷與限流

6、 使用Sentinel實現接口限流(在控制檯)

Spring Cloud Alibaba:Sentinel實現熔斷與限流

7、測試

通過上面的配置,實現的是/hello接口qps最大是2,如果qps大於2,則快速失敗,配置完成,點擊保存,我們快速刷新瀏覽器,會發現快速失敗

Spring Cloud Alibaba:Sentinel實現熔斷與限流

七、總結

本文主要介紹了Sentinel的概念、特性、與Hystrix的區別、一些核心概念和與SpringCloud的簡單整合。隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。 Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。


分享到:


相關文章: