Spring Cloud Alibaba - Sentinel 分布式系統流量哨兵

在這里插入圖片描述

目錄

  • 概述
    • 特征
    • 基本概念
  • 安裝Sentinel
  • 微服務引入Sentinel案例
  • 流控規則(流量控制)
    • 流控模式-直接
    • 流控模式-關聯
    • 流控模式-鏈路
    • 流控效果-快速失敗
    • 流控效果-預熱WarmUp
    • 流控效果-排隊等候
  • 流控規則(并發線程數控制)
  • 熔斷規則(熔斷降級)
    • 慢調用比例
      • 案例演示
    • 異常比例
      • 案例演示
    • 異常數
      • 案例演示
  • @SentinelResource注解
  • 熱點規則(熱點參數限流)
  • 授權規則(黑白名單控制)
  • 規則持久化(規則入Nacos)
  • OpenFeign和Sentinel集成實現fallback服務降級
  • GateWay和Sentinel集成實現服務限流
  • 相關文獻

概述

隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 是面向分布式、多語言異構化服務架構的流量治理組件,主要以流量為切入點,從流量路由、流量控制、流量整形、熔斷降級、系統自適應過載保護、熱點流量防護等多個維度來幫助開發者保障微服務的穩定性。

一句話簡單概括,Sentinel是一種流量治理的組件,作用等價于Spring Cloud Circurk Breaker。

特征

  • 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的范圍)、消息削峰填谷、集群流量控制、實時熔斷下游不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制臺中看到接入應用的單臺機器秒級數據,甚至 500 臺以下規模的集群的匯總運行情況。
  • 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只需要引入相應的依賴并進行簡單的配置即可快速地接入 Sentinel。同時 Sentinel 提供 Java/Go/C++ 等多語言的原生實現。
  • 完善的 SPI 擴展機制:Sentinel 提供簡單易用、完善的 SPI 擴展接口。您可以通過實現擴展接口來快速地定制邏輯。例如定制規則管理、適配動態數據源等。

Sentinel 的主要特性:
在這里插入圖片描述

基本概念

資源

資源是 Sentinel 的關鍵概念。它可以是 Java 應用程序中的任何內容,例如,由應用程序提供的服務,或由應用程序調用的其它應用提供的服務,甚至可以是一段代碼。在接下來的文檔中,我們都會用資源來描述代碼塊。

只要通過 Sentinel API 定義的代碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。

規則
圍繞資源的實時狀態設定的規則,可以包括流量控制規則、熔斷降級規則以及系統保護規則。所有規則可以動態實時調整。(Sentinel控制臺、yml配置、java代碼都可設定規則)

安裝Sentinel

Sentinel 的使用可以分為兩個部分:

  • 核心庫(Java 客戶端):不依賴任何框架/庫,能夠運行于 Java 8 及以上的版本的運行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支持。
  • 控制臺(Dashboard):Dashboard 主要負責管理推送規則、監控、管理機器信息等。

下載路徑:https://github.com/alibaba/Sentinel/releases

下載完后找到jar包,運行命令啟動Sentinel控制臺

java -jar sentinel-dashboard-xxx.jar

本地環境必須要jdk8版本以上,且8080端口未被占用(Sentinel 控制臺端口)
從 Sentinel 1.6.0 起,Sentinel 控制臺引入基本的登錄功能,默認用戶名和密碼都是 sentinel,訪問網址:http://localhost:8080
在這里插入圖片描述

微服務引入Sentinel案例

新建一個微服務,引入Nacos和Sentinel,將服務注冊進Nacos,對服務進行流量監控和熔斷降級

引入依賴

 <!--SpringCloud alibaba sentinel --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!--nacos-discovery--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>

修改yml配置

server:port: 8401spring:application:name: cloudalibaba-sentinel-servicecloud:nacos:discovery:server-addr: localhost:8848         #Nacos服務注冊中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制臺服務地址port: 8719 #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口

啟動類

@EnableDiscoveryClient
@SpringBootApplication
public class Main8401
{public static void main(String[] args){SpringApplication.run(Main8401.class,args);}
}

業務類

@RestController
public class FlowLimitController
{@GetMapping("/testA")public String testA(){return "------testA";}@GetMapping("/testB")public String testB(){return "------testB";}
}

啟動8401服務,這時候查看Sentinel控制臺發現什么都沒有,因為實際接口并未訪問,Sentinel采用的是懶加載,不訪問不監控,所以請求接口,http://localhost8401/testA,http://localhost:8401/testB,效果如下圖
在這里插入圖片描述

流控規則(流量控制)

在這里插入圖片描述

流量控制(flow control),其原理是監控應用流量的 QPS 或并發線程數等指標,當達到指定的閾值時對流量進行控制,以避免被瞬時的流量高峰沖垮,從而保障應用的高可用性,參數如下
在這里插入圖片描述

參數含義
資源名資源的唯一名稱,默認就是請求的接口路徑,可以自行修改,但是要保證唯一。
針對來源具體針對某個微服務進行限流,默認值為default,表示不區分來源,全部限流。
閾值類型QPS表示通過QPS進行限流,并發線程數表示通過并發線程數限流。
單機閾值與閾值類型組合使用。如果閾值類型選擇的是QPS,表示當調用接口的QPS達到閾值時,進行限流操作。如果閾值類型選擇的是并發線程數,則表示當調用接口的并發線程數達到閾值時,進行限流操作。
是否集群選中則表示集群環境,不選中則表示非集群環境。

流量控制主要有兩種統計類型,一種是統計并發線程數,另外一種則是統計 QPS,其中,0 代表根據并發數量來限流,1 代表根據 QPS 來進行流量控制

流控模式有三種:直接、關聯、鏈路

流控模式-直接

默認的流控模式,當接口達到限流條件時,直接開啟限流功能
表示1秒鐘內查詢1次就是OK,若超過次數1,就直接-快速失敗,報默認錯誤
在這里插入圖片描述
快速多次調用http://localhost8401/testA,會出現Blocked by Sentinel (flow limiting),限流成功

流控模式-關聯

當兩個資源之間具有資源爭搶或者依賴關系的時候,這兩個資源便具有了關聯。比如對數據庫同一個字段的讀操作和寫操作存在爭搶,讀的速度過高會影響寫得速度,寫的速度過高會影響讀的速度。如果放任讀寫操作爭搶資源,則爭搶本身帶來的開銷會降低整體的吞吐量。可使用關聯限流來避免具有關聯關系的資源之間過度的爭搶,舉例來說,read_db 和 write_db 這兩個資源分別代表數據庫讀寫,我們可以給 read_db 設置限流規則來達到寫優先的目的:設置 strategy 為 RuleConstant.STRATEGY_RELATE 同時設置 refResource 為 write_db。這樣當寫庫操作過于頻繁時,讀數據的請求會被限流。

簡單來說就是:B惹事,A掛了

控制臺演示配置:當配置關聯資源B的qps閾值超過1時,就限流A的訪問地址
在這里插入圖片描述
使用jmeter設置并發3秒內瘋狂訪問http://localhost:8401/testB,在不啟動jmeter之前訪問http://localhost:8401/testA,返回正常,這時候啟動jmeter
在這里插入圖片描述
在這里插入圖片描述
這時候再訪問/testA,會發現返回Blocked by Sentinel (flow limiting),等到jmeter調用結束后再訪問/testA服務限流結束可正常訪問,效果正確則配置成功

在這里我理解的是,AB服務之間,會優先確保B服務正常的一種限流策略,實際使用場景根據業務要求來設置。

流控模式-鏈路

來自不同鏈路請求對同一個目標訪問時,實施針對性的限流措施,比如C來訪問就限流,D來訪問不限流

修改8401微服務的yml,關鍵配置【web-context-unify】默認true,設置為false

server:port: 8401spring:application:name: cloudalibaba-sentinel-service #8401微服務提供者后續將會被納入阿里巴巴sentinel監管cloud:nacos:discovery:server-addr: localhost:8848         #Nacos服務注冊中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制臺服務地址port: 8719 #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口web-context-unify: false # controller層的方法對service層調用不認為是同一個根鏈路

新建service

@Service
public class FlowLimitService
{@SentinelResource(value = "common")public void common(){System.out.println("------FlowLimitService come in");}
}

controller新增方法

/**流控-鏈路演示demo* C和D兩個請求都訪問flowLimitService.common()方法,閾值到達后對C限流,對D不管*/@Resource private FlowLimitService flowLimitService;@GetMapping("/testC")public String testC(){flowLimitService.common();return "------testC";}@GetMapping("/testD")public String testD(){flowLimitService.common();return "------testD";}

控制臺新增配置,C和D兩個請求都訪問flowLimitService.common()方法,對C限流,對D不管
在這里插入圖片描述
訪問http://localhost:8401/testC,超過一秒鐘一次后,就發生限流
在這里插入圖片描述
http://localhost:8401/testD,正常訪問

流控效果-快速失敗

快速失敗也是直接拒絕(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式,是默認的流量控制方式,當QPS超過任意規則的閾值后,新的請求就會被立即拒絕,拒絕方式為拋出FlowException。這種方式適用于對系統處理能力確切已知的情況下,比如通過壓測確定了系統的準確水位時。

上面幾個流控規則演示的就是快速失敗案例。

流控效果-預熱WarmUp

當流量突然增大的時候,我們常常會希望系統從空閑狀態到繁忙狀態的切換的時間長一些。即如果系統在此之前長期處于空閑的狀態,我們希望處理請求的數量是緩步的增多,經過預期的時間以后,到達系統處理請求個數的最大值。Warm Up(冷啟動,預熱)模式就是為了實現這個目的的。

這個場景主要用于啟動需要額外開銷的場景,例如建立數據庫連接等。通常冷啟動的過程系統允許通過的 QPS 曲線如下圖所示:
在這里插入圖片描述
它的實現是在 Guava 的算法的基礎上實現的。默認 coldFactor(冷卻因子) 為 3(默認值),即請求 QPS 從 threshold / 3 開始,經預熱時長逐漸升至設定的 QPS 閾值。

源碼
在這里插入圖片描述
案例,單機閾值為10,預熱時長設置5秒。
系統初始化的閾值為10 / 3 約等于3,即單機閾值剛開始為3(我們人工設定單機閾值是10,sentinel計算后QPS判定為3開始);
然后過了5秒后閥值才慢慢升高恢復到設置的單機閾值10,也就是說5秒鐘內QPS為3,過了保護期5秒后QPS為10

控制臺配置
在這里插入圖片描述
多次點擊http://localhost:8401/testB,剛開始不行(被限流了),后面慢慢恢復就ok了

應用場景如:秒殺系統在開啟的瞬間,會有很多流量上來,很有可能把系統打死,預熱方式就是把為了保護系統,可慢慢的把流量放進來,慢慢的把閾值增長到設置的閾值。

流控效果-排隊等候

它的中心思想是,以固定的間隔時間讓請求通過。當請求到來的時候,如果當前請求距離上個通過的請求通過的時間間隔不小于預設值,則讓當前請求通過;否則,計算當前請求的預期通過時間,如果該請求的預期通過時間小于規則預設的 timeout 時間,則該請求會等待直到預設時間到來通過(排隊等待處理);若預期的通過時間超出最大排隊時長,則直接拒接這個請求。

這種方式適合用于請求以突刺狀來到,這個時候我們不希望一下子把所有的請求都通過,這樣可能會把系統壓垮;同時我們也期待系統以穩定的速度,逐步處理這些請求,以起到“削峰填谷”的效果,而不是拒絕所有請求。

例如,如果系統使用 Apache RocketMQ 來收發消息,系統在某個時間突然收到大量消息。我們希望以固定的速率來處理消息,而不是一下子拒絕這些消息。這個時候可以使用勻速器,也就是給消息排隊。效果如下所示:
在這里插入圖片描述
Sentinel 勻速排隊等待策略是 Leaky Bucket 算法結合虛擬隊列等待機制實現的。
注意:勻速排隊模式暫時不支持 QPS > 1000 的場景。

8401服務controller代碼新增

@GetMapping("/testE")
public String testE()
{System.out.println(System.currentTimeMillis()+"      testE,排隊等待");return "------testE";
}

jmeter
在這里插入圖片描述
Sentinel控制臺
在這里插入圖片描述
按照單機閾值,一秒鐘通過一個請求,10秒后的請求作為超時處理,放棄
在這里插入圖片描述

流控規則(并發線程數控制)

并發數控制用于保護業務線程池不被慢調用耗盡。例如,當應用所依賴的下游應用由于某種原因導致服務不穩定、響應延遲增加,對于調用者來說,意味著吞吐量下降和更多的線程數占用,極端情況下甚至導致線程池耗盡。為應對太多線程占用的情況,業內有使用隔離的方案,比如通過不同業務邏輯使用不同線程池來隔離業務自身之間的資源爭搶(線程池隔離)。這種隔離方案雖然隔離性比較好,但是代價就是線程數目太多,線程上下文切換的 overhead 比較大,特別是對低延時的調用有比較大的影響。Sentinel 并發控制不負責創建和管理線程池,而是簡單統計當前請求上下文的線程數目(正在執行的調用數目),如果超出閾值,新的請求會被立即拒絕,效果類似于信號量隔離。并發數控制通常在調用端進行配置。
在這里插入圖片描述

結合上圖,簡單來說就是,Sentinel設置的單機閾值為1,用jmeter去設置10s并發請求/testB,這時候如果jmeter沒有結束請求線程一直在占用的話,此時我們再用其他方式去調用/testB,是直接拒絕的效果,所以有且只有一個直接拒絕的流控效果。

熔斷規則(熔斷降級)

除了流量控制以外,對調用鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。一個服務常常會調用別的模塊,可能是另外的一個遠程服務、數據庫,或者第三方 API 等。例如,支付的時候,可能需要遠程調用銀聯提供的 API;查詢某個商品的價格,可能需要進行數據庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那么調用服務的方法的響應時間也會變長,線程會產生堆積,最終可能耗盡業務自身的線程池,服務本身也變得不可用。

現代微服務架構都是分布式的,由非常多的服務組成。不同服務之間相互調用,組成復雜的調用鏈路。以上的問題在鏈路調用中會產生放大的效果。復雜鏈路上的某一環不穩定,就可能會層層級聯,最終導致整個鏈路都不可用。因此我們需要對不穩定的弱依賴服務調用進行熔斷降級,暫時切斷不穩定調用,避免局部不穩定因素導致整體的雪崩。熔斷降級作為保護自身的手段,通常在客戶端(調用端)進行配置。
在這里插入圖片描述

Sentinel 提供以下幾種熔斷策略:

  • 慢調用比例 (SLOW_REQUEST_RATIO)
  • 異常比例 (ERROR_RATIO)
  • 異常數 (ERROR_COUNT)

慢調用比例

選擇以慢調用比例作為閾值,需要設置允許的慢調用 RT(即最大的響應時間),請求的響應時間大于該值則統計為慢調用。當單位統計時長(statIntervalMs)內請求數目大于設置的最小請求數目,并且慢調用的比例大于閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小于設置的慢調用 RT 則結束熔斷,若大于設置的慢調用 RT 則會再次被熔斷。

看圖理解:
graphic
1.調用:一個請求發送到服務器,服務器給與響應,一個響應就是一個調用。
2.最大RT:即最大的響應時間,指系統對請求作出響應的業務處理時間。
3.慢調用:處理業務邏輯的實際時間>設置的最大RT時間,這個調用叫做慢調用。
4.慢調用比例:在所以調用中,慢調用占有實際的比例=慢調用次數?總調用次數
5.比例閾值:自己設定的 , 比例閾值=慢調用次數?調用次數
6.統計時長:時間的判斷依據
7.最小請求數:設置的調用最小請求數,上圖比如1秒鐘打進來10個線程(大于我們配置的5個了)調用被觸發

進入熔斷狀態判斷依據:在統計時長內,實際請求數目>設定的最小請求數 且 實際慢調用比例>比例閾值 ,進入熔斷狀態。

1熔斷狀態(保險絲跳閘斷電,不可訪問):在接下來的熔斷時長內請求會自動被熔斷

2探測恢復狀態(探路先鋒):熔斷時長結束后進入探測恢復狀態

3結束熔斷(保險絲閉合恢復,可以訪問):在探測恢復狀態,如果接下來的一個請求響應時間小于設置的慢調用 RT,則結束熔斷,否則繼續熔斷。

案例演示

10個線程,在一秒的時間內發送完。又因為服務器響應時長設置:暫停1秒,所以響應一個請求的時長都大于1秒綜上符合熔斷條件,所以當線程開啟1秒后,進入熔斷狀態

8401服務controller修改

/*** 新增熔斷規則-慢調用比例* @return*/
@GetMapping("/testF")
public String testF()
{//暫停幾秒鐘線程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }System.out.println("----測試:新增熔斷規則-慢調用比例 ");return "------testF 新增熔斷規則-慢調用比例";
}

控制臺配置
在這里插入圖片描述
jmeter壓測
在這里插入圖片描述
按照上述配置,熔斷觸發:

多次循環,一秒鐘打進來10個線程(大于5個了)調用/testF

假如在統計時長內,實際請求數目>最小請求數且慢調用比例>比例閾值 ,斷路器打開(保險絲跳閘)微服務不可用(Blocked by Sentinel (flow limiting)),進入熔斷狀態5秒;后續停止jmeter,沒有這么大的訪問量了,單獨用瀏覽器訪問rest地址,斷路器關閉(保險絲恢復,合上閘口),

微服務恢復OK

異常比例

當單位統計時長(statIntervalMs)內請求數目大于設置的最小請求數目,并且異常的比例大于閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值范圍是 [0.0, 1.0],代表 0% - 100%。
在這里插入圖片描述

案例演示

controller

/*** 新增熔斷規則-異常比例* @return*/
@GetMapping("/testG")
public String testG()
{System.out.println("----測試:新增熔斷規則-異常比例 ");int age = 10/0;return "------testG,新增熔斷規則-異常比例 ";
}

控制臺
在這里插入圖片描述
jmeter
在這里插入圖片描述
啟動jmeter后訪問/testG,配置成功
在這里插入圖片描述

異常數

當單位統計時長內的異常數目超過閾值之后會自動進行熔斷。經過熔斷時長后熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。

案例演示

controller

/*** 新增熔斷規則-異常數* @return*/
@GetMapping("/testH")
public String testH()
{System.out.println("----測試:新增熔斷規則-異常數 ");int age = 10/0;return "------testH,新增熔斷規則-異常數 ";
}

控制臺
在這里插入圖片描述
jmeter
在這里插入圖片描述
啟動jmeter開工,上述配置表示,在1秒鐘內最少請求2次,當異常數大于1時,會觸發熔斷操作斷路器開啟(保險絲跳閘),微服務不可用了,熔斷的時長為5秒,不再報錯error而是服務降級了出提示Blocked by Sentinel (flow limiting)

@SentinelResource注解

SentinelResource是一個流量防衛防護組件注解用于指定防護資源,對配置的資源進行流量控制、熔斷降級等功能,

查看SentinelResource 源碼,可以得知有三個屬性很重要

  • value:資源名稱
  • blockHandler:處理BlockException的函數名稱,函數要求1. 必須是 public2.返回類型 參數與原方法一致3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置blockHandlerClass ,并指定blockHandlerClass里面的方法。
  • fallback:用于在拋出異常的時候提供fallback處理邏輯。 fallback函數可以針對所有類型的異常(除了 exceptionsToIgnore 里面排除掉的異常類型)進行處理。函數要求:1. 返回類型與原方法一致2. 參數類型需要和原方法相匹配3. 默認需和原方法在同一個類中。若希望使用其他類的函數,可配置fallbackClass ,并指定fallbackClass里面的方法。

代碼演示(Sentinel控制臺自行創建熔斷規則,上面有很多案例演示,本次演示就不多說了)

@GetMapping("/rateLimit/doAction/{p1}")@SentinelResource(value = "doActionSentinelResource",blockHandler = "doActionBlockHandler", fallback = "doActionFallback")public String doAction(@PathVariable("p1") Integer p1) {if (p1 == 0){throw new RuntimeException("p1等于零直接異常");}return "doAction";}public String doActionBlockHandler(@PathVariable("p1") Integer p1,BlockException e){log.error("sentinel配置自定義限流了:{}", e);return "sentinel配置自定義限流了";}public String doActionFallback(@PathVariable("p1") Integer p1,Throwable e){log.error("程序邏輯異常了:{}", e);return "程序邏輯異常了"+"\t"+e.getMessage();}

可見代碼上面配置了三個屬性,分三種場景來說明

  1. 只設置了value:限流生效的話,默認返回限流提示語Blocked by Sentinel (flow limiting)
  2. 設置value+blockHandler:限流生效,返回自定義提示語如:sentinel配置自定義限流了
  3. 設置value+blockHandler+fallback:限流生效,返回自定義提示語如:sentinel配置自定義限流了,但如果程序出現運行時異常,提示自定義語句:程序邏輯異常了xxxxx

blockHandler,主要針對sentinel配置后出現的違規情況處理
fallback,程序異常了JVM拋出的異常服務降級
兩者可共存,具體案例演示可自行編寫測試

熱點規則(熱點參數限流)

何為熱點?熱點即經常訪問的數據。很多時候我們希望統計某個熱點數據中訪問頻次最高的 Top K 數據,并對其訪問進行限制。比如:

  • 商品 ID 為參數,統計一段時間內最常購買的商品 ID 并進行限制
  • 用戶 ID 為參數,針對一段時間內頻繁訪問的用戶 ID 進行限制

服務新增接口

@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey",blockHandler = "dealHandler_testHotKey")
public String testHotKey(@RequestParam(value = "p1",required = false) String p1, @RequestParam(value = "p2",required = false) String p2){return "------testHotKey";
}
public String dealHandler_testHotKey(String p1,String p2,BlockException exception)
{return "-----dealHandler_testHotKey";
}

控制臺
在這里插入圖片描述
限流模式只支持QPS模式,固定寫死了。

@SentinelResource注解的方法參數索引,0代表第一個參數,1代表第二個參數,以此類推

單機閥值以及統計窗口時長表示在此窗口時間超過閥值就限流。

上面的抓圖就是第一個參數有值的話,1秒的QPS為1,超過就限流,限流后調用dealHandler_testHotKey支持方法。

效果

  • 訪問http://localhost:8401/testHotKey?p1=abc,含有參數P1,當每秒訪問的頻率超過1次時,會觸發Sentinel的限流操作
  • 訪問http://localhost:8401/testHotKey?p1=abc&p2=33,當每秒訪問的頻率超過1次時,會觸發Sentinel的限流操作
  • 訪問http://localhost:8401/testHotKey?p2=33,沒有熱點參數P1,不會被限流正常訪問

參數例外項
如果我們希望P1能夠對指定數值進行限流也是可以的。
在這里插入圖片描述
效果:

  • 訪問http://localhost:8401/testHotKey?p1=5,當p1等于5的時候,閾值變為200,達到200閾值后才會被限流
  • 訪問http://localhost:8401/testHotKey?p1=2,當p1等于2的時候,閾值還是1立馬限流

熱點參數的注意點,參數必須是基本類型或者String

授權規則(黑白名單控制)

很多時候,我們需要根據調用來源來判斷該次請求是否允許放行,這時候可以使用 Sentinel 的來源訪問控制(黑白名單控制)的功能。來源訪問控制根據資源的請求來源(origin)限制資源是否通過,若配置白名單則只有請求來源位于白名單內時才可通過;若配置黑名單則請求來源位于黑名單時不通過,其余的請求通過。

案例演示
新建controller

@RestController
@Slf4j
public class EmpowerController //Empower授權規則,用來處理請求的來源
{@GetMapping(value = "/empower")public String requestSentinel4(){log.info("測試Sentinel授權規則empower");return "Sentinel授權規則";}
}

新建配置類

@Component
public class MyRequestOriginParser implements RequestOriginParser
{@Overridepublic String parseOrigin(HttpServletRequest httpServletRequest) {return httpServletRequest.getParameter("serverName");}
}

啟動8401服務,訪問http://localhost:8401/empower成功
控制臺
在這里插入圖片描述
效果

  • 訪問http://localhost:8401/empower?serverName=test,http://localhost:8401/empower?serverName=test2被限流,顯示限流默認提示語
    • 訪問http://localhost:8401/empower?serverName=aa,正常返回數據

因為test和test2在控制臺被設置了黑名單,因此其他的才能訪問成功

規則持久化(規則入Nacos)

到此為止,如果服務一旦重啟,Sentinel控制臺配置的規則就會被重置,因此需要將限流配置規則持久化進Nacos保存,只要刷新8401某個rest地址,Sentinel控制臺的流控規則就能看到,只要Nacos里面的配置不刪除,針對8401上Sentinel上的流控規則持續有效

修改8401服務,引入Nacos依賴

<!--SpringCloud ailibaba sentinel-datasource-nacos --><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-datasource-nacos</artifactId></dependency>

修改yml

server:port: 8401spring:application:name: cloudalibaba-sentinel-service #8401微服務提供者后續將會被納入阿里巴巴sentinel監管cloud:nacos:discovery:server-addr: localhost:8848         #Nacos服務注冊中心地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制臺服務地址port: 8719 #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口web-context-unify: false # controller層的方法對service層調用不認為是同一個根鏈路datasource:ds1:nacos:server-addr: localhost:8848dataId: ${spring.application.name}groupId: DEFAULT_GROUPdata-type: jsonrule-type: flow # com.alibaba.cloud.sentinel.datasource.RuleType

rule-type含義值見源碼
在這里插入圖片描述
在這里插入圖片描述
把規則配置添加到Nacos
在這里插入圖片描述

resource:資源名稱;
limitApp:來源應用;
grade:閾值類型,0表示線程數,1表示QPS;
count:單機閾值;
strategy:流控模式,0表示直接,1表示關聯,2表示鏈路;
controlBehavior:流控效果,0表示快速失敗,1表示Warm Up,2表示排隊等待;
clusterMode:是否集群。

這時候停止8401,Sentinel控制臺沒有規則(被重置了),重啟8401,調用8401服務接口,過一會,配置的規則就會出來,規則持久化生效。

OpenFeign和Sentinel集成實現fallback服務降級

每個微服務都有自己的fallback服務降級處理,會有幾個問題

  • fallback過多,不易管理
  • 代碼不整潔(每個controller的api都有自己的fallback,重復代碼過多)

那么可以使用FeignClient的fallback,走統一的服務降級

本次案例三個微服務,nacos-payment-provider(服務提供方)、cloud-api-commons(統一的服務降級處理)、nacos-order-consumer(服務調用方)

服務提供方
引入依賴

<!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency><!-- 引入自己定義的api通用包 --><dependency><groupId>com.demo.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency>

配置yml

server:port: 9001spring:application:name: nacos-payment-providercloud:nacos:discovery:server-addr: localhost:8848 #配置Nacos地址sentinel:transport:dashboard: localhost:8080 #配置Sentinel dashboard控制臺服務地址port: 8719 #默認8719端口,假如被占用會自動從8719開始依次+1掃描,直至找到未被占用的端口

啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class Main9001
{public static void main(String[] args){SpringApplication.run(Main9001.class,args);}
}

controller

@Value("${server.port}")private String serverPort;@GetMapping(value = "/pay/nacos/{id}")public String getPayInfo(@PathVariable("id") Integer id){return "nacos registry, serverPort: "+ serverPort+"\t id"+id;}@GetMapping("/pay/nacos/get/{orderNo}")@SentinelResource(value = "getPayByOrderNo",blockHandler = "handlerBlockHandler")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo){//模擬從數據庫查詢出數據并賦值給DTOPayDTO payDTO = new PayDTO();payDTO.setId(1024);payDTO.setOrderNo(orderNo);payDTO.setAmount(BigDecimal.valueOf(9.9));payDTO.setPayNo("pay:"+IdUtil.fastUUID());payDTO.setUserId(1);return ResultData.success("查詢返回值:"+payDTO);}public ResultData handlerBlockHandler(@PathVariable("orderNo") String orderNo,BlockException exception){return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"getPayByOrderNo服務不可用," +"觸發sentinel流控配置規則"+"\t"+"o(╥﹏╥)o");}

啟動項目,調用接口自測,http://localhost:9001/pay/nacos/get/1024

cloud-api-commons
引入依賴

<!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

controller

@FeignClient(value = "nacos-payment-provider",fallback = PayFeignSentinelApiFallBack.class)
public interface PayFeignSentinelApi
{@GetMapping("/pay/nacos/get/{orderNo}")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}

為遠程調用新建全局統一服務降級類

@Component
public class PayFeignSentinelApiFallBack implements PayFeignSentinelApi
{@Overridepublic ResultData getPayByOrderNo(String orderNo){return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"對方服務宕機或不可用,FallBack服務降級o(╥﹏╥)o");}
}

服務調用方
引入依賴

<!-- 引入自己定義的api通用包 --><dependency><groupId>com.demo.cloud</groupId><artifactId>cloud-api-commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--openfeign--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><!--alibaba-sentinel--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency>

修改yml

server:port: 83spring:application:name: nacos-order-consumercloud:nacos:discovery:server-addr: localhost:8848
#消費者將要去訪問的微服務名稱(nacos微服務提供者叫什么你寫什么)
service-url:nacos-user-service: http://nacos-payment-provider# 激活Sentinel對Feign的支持
feign:sentinel:enabled: true

啟動類

@EnableDiscoveryClient
@SpringBootApplication
@EnableFeignClients
public class Main83
{public static void main(String[] args){SpringApplication.run(Main83.class,args);}
}

controller

@RestController
public class OrderNacosController
{@Resourceprivate PayFeignSentinelApi payFeignSentinelApi;@GetMapping(value = "/consumer/pay/nacos/get/{orderNo}")public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo){return payFeignSentinelApi.getPayByOrderNo(orderNo);}
}

啟動消費者服務,如果出現以下報錯,檢查springboot和springcloud的版本問題,會導致Sentinel版本不兼容
在這里插入圖片描述
這樣兩個服務都啟動了,調用http://lochost:83/consumer/pay/nacos/get/1024,正常返回
這時候Sentinel控制臺設置qps流量規則
在這里插入圖片描述
多次高頻調用http://lochost:83/consumer/pay/nacos/get/1024,返回【getPayByOrderNo服務不可用,觸發sentinel流控配置規則】,出發限流成功。

但是大家很好奇,為什么我的服務降級沒有提示呢,這時候把9001也就是服務提供者給停掉,再去調用http://lochost:83/consumer/pay/nacos/get/1024,就會提示【對方服務宕機或不可用,FallBack服務降級o(╥﹏╥)o】

GateWay和Sentinel集成實現服務限流

新建網關服務
pom

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-transport-simple-http</artifactId><version>1.8.6</version></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-spring-cloud-gateway-adapter</artifactId><version>1.8.6</version></dependency><dependency><groupId>javax.annotation</groupId><artifactId>javax.annotation-api</artifactId><version>1.3.2</version><scope>compile</scope></dependency>

yml

server:port: 9528spring:application:name: cloudalibaba-sentinel-gateway     # sentinel+gataway整合Casecloud:nacos:discovery:server-addr: localhost:8848gateway:routes:- id: pay_routh1 #pay_routh1                #路由的ID(類似mysql主鍵ID),沒有固定規則但要求唯一,建議配合服務名uri: http://localhost:9001                #匹配后提供服務的路由地址predicates:- Path=/pay/**                      # 斷言,路徑相匹配的進行路由

啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class Main9528
{public static void main(String[] args){SpringApplication.run(Main9528.class,args);}
}

config類

@Configuration
public class GatewayConfiguration {private final List<ViewResolver> viewResolvers;private final ServerCodecConfigurer serverCodecConfigurer;public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer){this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);this.serverCodecConfigurer = serverCodecConfigurer;}@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {// Register the block exception handler for Spring Cloud Gateway.return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);}@Bean@Order(-1)public GlobalFilter sentinelGatewayFilter() {return new SentinelGatewayFilter();}@PostConstruct //javax.annotation.PostConstructpublic void doInit() {initBlockHandler();}//處理/自定義返回的例外信息private void initBlockHandler() {Set<GatewayFlowRule> rules = new HashSet<>();rules.add(new GatewayFlowRule("pay_routh1").setCount(2).setIntervalSec(1));GatewayRuleManager.loadRules(rules);BlockRequestHandler handler = new BlockRequestHandler() {@Overridepublic Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {Map<String,String> map = new HashMap<>();map.put("errorCode", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());map.put("errorMessage", "請求太過頻繁,系統忙不過來,觸發限流(sentinel+gataway整合Case)");return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS).contentType(MediaType.APPLICATION_JSON).body(BodyInserters.fromValue(map));}};GatewayCallbackManager.setBlockHandler(handler);}}

config參考官網配置
在這里插入圖片描述
測試:
啟動9001服務、9528網關,訪問http://localhost:9001/pay/nacos/333成功,訪問http://localhost:9528/pay/nacos/333也成功,如果http://localhost:9528/pay/nacos/333訪問并發起來了,就會提示【請求太過頻繁,系統忙不過來,觸發限流(sentinel+gataway整合Case)】

相關文獻

Sentinel github:https://github.com/alibaba/Sentinel
Sentinel官網:https://sentinelguard.io/zh-cn/docs/introduction.html

就先說到這 \color{#008B8B}{ 就先說到這} 就先說到這
在下 A p o l l o \color{#008B8B}{在下Apollo} 在下Apollo
一個愛分享 J a v a 、生活的小人物, \color{#008B8B}{一個愛分享Java、生活的小人物,} 一個愛分享Java、生活的小人物,
咱們來日方長,有緣江湖再見,告辭! \color{#008B8B}{咱們來日方長,有緣江湖再見,告辭!} 咱們來日方長,有緣江湖再見,告辭!

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/39471.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/39471.shtml
英文地址,請注明出處:http://en.pswp.cn/web/39471.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

AndroidStudio的switch-case語句報錯解決

大家好&#xff0c;我是咕嚕鐵蛋。在Android開發的道路上&#xff0c;我們總會遇到各種各樣的問題&#xff0c;其中之一就是switch-case語句的報錯。今天&#xff0c;我就和大家分享一下在AndroidStudio中遇到switch-case語句報錯時&#xff0c;我們應該如何排查和解決這些問題…

Kotlin 處理livedata數據倒灌

LiveData 數據倒灌問題通常指的是在訂閱者注冊后立即接收到之前發送的數據。這個行為在某些場景下是需要的&#xff0c;但在某些情況下可能是不希望的。 主要有兩種中方法&#xff1a; 1 使用 SingleLiveEvent--------SingleLiveEvent 是一個自定義的 LiveData 類&#xff0c…

精雕細琢:Postman中請求體的設置藝術

精雕細琢&#xff1a;Postman中請求體的設置藝術 在API測試與開發的廣闊天地中&#xff0c;Postman以其強大的功能和用戶友好的界面成為了探索這一領域的必備工具。而在構建API請求的過程中&#xff0c;請求體&#xff08;Body&#xff09;的設置無疑是傳達數據給服務器的關鍵…

Django 安裝 Zinnia 后出現故障

在Django中安裝和配置Zinnia時遇到故障可能有多種原因&#xff0c;通常包括版本兼容性、依賴關系或配置問題。這里提供一些常見的解決方法和調試步驟&#xff0c;幫助大家解決問題。 首先&#xff0c;確保您安裝的Zinnia版本與Django版本兼容。查看Zinnia的官方文檔或GitHub頁…

Linux庫概念及相關編程(動態庫-靜態庫)

Linux庫概念及相關編程 分文件編程案例 分文件編程是指將程序按功能模塊劃分成不同的文件進行編寫&#xff0c;這種方法有以下好處&#xff1a; 功能責任劃分&#xff1a;每個文件對應一個功能模塊&#xff0c;職責明確&#xff0c;易于理解和維護。方便調試&#xff1a;可以…

三、c++ qt 實現一個基于tcp的Session

在Qt框架下實現一個基于TCP的Session管理,你可以利用Qt的網絡模塊QTcpServer和QTcpSocket。下面是一個簡單的示例,展示了如何建立一個服務器,接收客戶端連接,并為每個連接的客戶端創建一個Session對象來管理會話。 首先,你需要包含必要的Qt頭文件,并定義一個TcpSession類…

C++左值/右值/左值引用/右值引用

1&#xff09;C入門級小知識&#xff0c;分享給將要學習或者正在學習C開發的同學。 2&#xff09;內容屬于原創&#xff0c;若轉載&#xff0c;請說明出處。 3&#xff09;提供相關問題有償答疑和支持。 左值和右值的概念&#xff1a; 早期的c語言中關于左值和右值的定義&a…

Kithara常見問題解答

目錄 通用問題我的內核驅動程序已經簽名了嗎&#xff1f;是否可以在打開驅動程序時防止顯示介紹窗口&#xff1f;Windows 7 仍然支持嗎&#xff1f;錯誤0x10142422(KSERROR_CANNOT_START_KERNEL)在KS_openDriver時出現&#xff1f;錯誤 10145241 (KSERROR_CANNOT_START_KERNEL)…

低代碼開發技術助力企業數字化管理的實踐探究

隨著信息技術的飛速發展&#xff0c;企業對于數字化管理的需求日益迫切。而低代碼開發技術&#xff0c;以其高效、靈活、易用的特點&#xff0c;正逐漸成為企業數字化管理的重要工具。本文將進一步探討低代碼開發技術在企業數字化管理實踐中的應用及其帶來的變革。 低代碼開發技…

每日一題——Python實現PAT乙級1026 程序運行時間(舉一反三+思想解讀+逐步優化)五千字好文

一個認為一切根源都是“自己不夠強”的INTJ 個人主頁&#xff1a;用哲學編程-CSDN博客專欄&#xff1a;每日一題——舉一反三Python編程學習Python內置函數 Python-3.12.0文檔解讀 目錄 我的寫法 代碼結構和邏輯 時間復雜度 空間復雜度 代碼優化建議 總結 我要更強 …

交換機需要多大 buffer

有點違背直覺&#xff0c;但是真事兒&#xff0c;交換機過境的流越多&#xff0c;所需 buffer 越小&#xff0c;這是為什么&#xff1f; 范氏(范雅各布森&#xff0c;van jacobson)管道的 aimd 流建議 buffer_size 為 bdp&#xff0c;這很容易理解&#xff0c;因為 aimd 流最小…

【mybatis】spring boot框架中使用mybatis-plus配置多數據源

1、簡介 在Java開發中&#xff0c;當使用MyBatis-Plus進行數據庫操作時&#xff0c;可能會遇到需要配置多數據源的場景&#xff0c;比如讀寫分離、操作多個數據庫等。MyBatis-Plus本身是基于MyBatis的增強工具&#xff0c;它并沒有直接提供多數據源的配置支持&#xff0c;但可…

k8s nfs配置mysql,redis

1.安裝nfs # centos yum install nfs-utils -ysudo mkdir -p /nfs/mysql sudo chmod 777 /nfs/mysqlsudo mkdir -p /nfs/redis sudo chmod 777 /nfs/redisvim /etc/exports/nfs/mysql *(rw,sync,no_root_squash) /nfs/redis *(rw,sync,no_root_squash)sudo exportfs -asystemc…

幣界網訊,預計以太坊現貨 ETF 將于 7 月中旬推出

剛剛 ETF Store 總裁 Nate Geraci 在 X &#xff08;前Twitter&#xff09;平臺上宣布&#xff0c;備受數字貨幣市場期待的SEC以太坊現貨 ETF提案&#xff0c;將于7 月中旬通過美國證券交易委員會&#xff08;SEC&#xff09;批準。Nate Geraci透露修訂后的 S-1 文件將于 7 月 …

cannot import name ‘LineString‘ from ‘shapely‘

cannot import name LineString from shapely pip install shapely -U 升級到2.0后好像好了

【VUE】 深入理解 Vue 動態路由:簡介、實際開發場景與代碼示例

深入理解 Vue 動態路由&#xff1a;簡介、實際開發場景與代碼示例 Vue.js 是一個用于構建用戶界面的漸進式框架&#xff0c;它擁有豐富的生態系統&#xff0c;其中 Vue Router 是其官方的路由管理庫。動態路由是 Vue Router 的一個強大特性&#xff0c;允許我們在應用運行時根…

pnpm的坑

請問pnpm的兩個坑怎么解決&#xff1a; 第一個坑&#xff1a;沒有節省磁盤空間 我已經配置了依賴的存儲位置&#xff0c; 但我在項目里pnpm install以后&#xff0c;發現依賴包還是很大&#xff0c; 然后發現里面的鏈接并不是指向先前配置的依賴存儲位置&#xff0c;而是指…

【數智化人物展】袋鼠云CEO寧海元:大模型時代,Data+AI將成為新的基礎設施

寧海元 本文由袋鼠云CEO寧海元投遞并參與由數據猿聯合上海大數據聯盟共同推出的《2024中國數智化轉型升級先鋒人物》榜單/獎項評選。 大數據產業創新服務媒體 ——聚焦數據 改變商業 身處這個瞬息萬變的數字經濟時代&#xff0c;傳統的生產模式往往依賴于經驗和固定的流程&…

上海市計算機學會競賽平臺2023年2月月賽丙組圓環三染色

題目描述 有一個圓環上有 &#x1d45b;n 個點&#xff0c;一個染色方案需要為每個點分配三種顏色中的一種&#xff0c;且圓環上相鄰的點顏色不能相同。 請求出有多少種染色方案。答案可能很大&#xff0c;輸出模 1,000,000,0071,000,000,007 的余數。 輸入格式 單個整數表…

k8s-第六節-數據持久化

數據持久化 kubernetes 集群不會為你處理數據的存儲&#xff0c;需要為數據庫掛載一個磁盤來確保數據的安全。 可以選擇云存儲、本地磁盤、NFS。 本地磁盤&#xff1a;可以掛載某個節點上的目錄&#xff0c;但是這需要限定 pod 在這個節點上運行 云存儲&#xff1a;不限定節…