一窑眯、前言:和Hystrix作對(duì)比
Hystrix:
- 1、需要我們自己手工搭建監(jiān)控平臺(tái)
- 2医窿、沒(méi)有一套web界面磅甩,不可以給我們進(jìn)行更加細(xì)粒度化的配置流控、速率控制姥卢、服務(wù)熔斷卷要、服務(wù)降級(jí)
Sentinel:
- 1、單獨(dú)一個(gè)組件独榴,可以獨(dú)立出來(lái)
- 2僧叉、直接界面化的細(xì)粒度統(tǒng)一配置
二、Sentinel(哨兵)是什么
官網(wǎng):https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D
隨著微服務(wù)的流行棺榔,服務(wù)和服務(wù)之間的穩(wěn)定性變得越來(lái)越重要瓶堕。Sentinel 以流量為切入點(diǎn),從流量控制症歇、熔斷降級(jí)郎笆、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度保護(hù)服務(wù)的穩(wěn)定性。主要就是服務(wù)雪崩忘晤、服務(wù)降級(jí)题画、服務(wù)熔斷、服務(wù)限流
三德频、Sentinel的下載安裝運(yùn)行
Sentinel分為兩部分:
核心庫(kù)(java客戶端):不依賴任何框架/庫(kù)苍息,能夠運(yùn)行于所有java運(yùn)行環(huán)境,同時(shí)對(duì)Dubbo/Spring Cloud等框架也有較好的支持壹置。
控制臺(tái)(Dashboard):基于Spring Boot開(kāi)發(fā)竞思,打包后可以直接運(yùn)行,不需要額外的Tomcat等應(yīng)用容器钞护。
下載Sentinel的jar包盖喷,然后直接java -jar 運(yùn)行即可 ,Sentinel的用戶和密碼默認(rèn)都是Sentinel
四难咕、Sentinel的初始化監(jiān)控
1课梳、建立一個(gè)module被Sentinel保護(hù)
2、pom
//sentinel的依賴
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
//后面做持久化會(huì)用到
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
//添加openfeign的依賴余佃,后面會(huì)用到
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
3暮刃、yml
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服務(wù)注冊(cè)中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默認(rèn)8719端口,假如被占用會(huì)自動(dòng)從8719+1依次掃描
port: 8719
management:
endpoints:
web:
exposure:
include: '*'
4爆土、主啟動(dòng)類
@SpringBootApplication
@EnableDiscoveryClient
public class MainApp8401 {
public static void main(String[] args) {
SpringApplication.run(MainApp8401.class,args);
}
}
5椭懊、業(yè)務(wù)類
@RestController
public class FlowLimitController {
@GetMapping("/testA")
public String testA() {
return "testA";
}
@GetMapping("/testB")
public String testB() {
return "testB";
}
}
6、測(cè)試
此時(shí)訪問(wèn)一下controller中指定的路徑步势,Sentinel就將對(duì)應(yīng)用進(jìn)行保護(hù)了氧猬,因?yàn)镾entinel采取的是懶加載背犯,訪問(wèn)localhost:8080也就可以看到應(yīng)用。
五盅抚、Sentinel的流量控制
流量控制(flow control)漠魏,其原理是監(jiān)控應(yīng)用流量的 QPS 或并發(fā)線程數(shù)等指標(biāo),當(dāng)達(dá)到指定的閾值時(shí)對(duì)流量進(jìn)行控制妄均,以避免被瞬時(shí)的流量高峰沖垮柱锹,從而保障應(yīng)用的高可用性。
資源名:唯一名稱丛晦,默認(rèn)請(qǐng)求路徑
閾值類型/單機(jī)閾值:
①Q(mào)PS(每秒鐘的請(qǐng)求數(shù)量):當(dāng)調(diào)用該api的QPS達(dá)到閾值的時(shí)候,進(jìn)行限流
②線程數(shù):當(dāng)調(diào)用該api的線程數(shù)達(dá)到閾值的時(shí)候進(jìn)行限流
流控模式:
①直接:api達(dá)到限流條件時(shí)提陶,直接限流
②關(guān)聯(lián):當(dāng)關(guān)聯(lián)的資源達(dá)到閾值時(shí)烫沙,就限流自己。
③鏈路:只記錄指定鏈路上的流量隙笆,如果達(dá)到閾值锌蓄,就進(jìn)行限流
流控效果
①快速失敗:直接失敗撑柔,拋異常
②Warm up:根據(jù)codeFactor(冷加載因子瘸爽,默認(rèn)3)的值,從閾值/codeFactor铅忿,經(jīng)過(guò)預(yù)熱時(shí)長(zhǎng)剪决,才達(dá)到設(shè)置的QPS閾值
③排隊(duì)等待:勻速排隊(duì),讓請(qǐng)求以勻速的速度通過(guò)檀训,閾值類型必須為QPS柑潦,否則無(wú)效
1、QPS直接快速失敗報(bào)錯(cuò)
2渗鬼、QPS和線程作對(duì)比
如下圖所示,在這里都以設(shè)置為1作比較荧琼,QPS表示的是每秒的請(qǐng)求數(shù)量譬胎,如果一旦一秒鐘超過(guò)了1個(gè),就相當(dāng)于是有一扇門(mén)直接給擋住了命锄,然后直接報(bào)錯(cuò)堰乔。線程就像是坝撑,進(jìn)去了這扇門(mén)斟赚,不管有多少數(shù)據(jù)都可以進(jìn)來(lái),但是只能一個(gè)個(gè)的依次的來(lái)處理陵霉,就像是銀行的業(yè)務(wù)人員被盈,一旦兩個(gè)想同時(shí)訪問(wèn)析孽,不好意思搭伤,不行。
3袜瞬、QPS關(guān)聯(lián)模式快速報(bào)錯(cuò)(因?yàn)槲⒎?wù)中兩個(gè)服務(wù)之間是有關(guān)聯(lián)的怜俐,比如說(shuō)支付接口掛了,那你下單接口也歇一歇)當(dāng)與A關(guān)聯(lián)的B達(dá)到閾值時(shí)邓尤,A就限流自己拍鲤,B惹事A遭殃
@GetMapping("/testA")
public String testA() {
return "testA";
}
@GetMapping("/testB")
public String testB() {
return "testB";
}
4、QPS warm up流控效果
Warm Up(
RuleConstant.CONTROL_BEHAVIOR_WARM_UP
)方式汞扎,即預(yù)熱/冷啟動(dòng)方式季稳。當(dāng)系統(tǒng)長(zhǎng)期處于低水位的情況下,當(dāng)流量突然增加時(shí)澈魄,直接把系統(tǒng)拉升到高水位可能瞬間把系統(tǒng)壓垮景鼠。通過(guò)"冷啟動(dòng)",讓通過(guò)的流量緩慢增加痹扇,在一定時(shí)間內(nèi)逐漸增加到閾值上限铛漓,給冷系統(tǒng)一個(gè)預(yù)熱的時(shí)間,避免冷系統(tǒng)被壓垮鲫构。詳細(xì)文檔可以參考 流量控制 - Warm Up 文檔浓恶,具體的例子可以參見(jiàn) WarmUpFlowDemo。
通常冷啟動(dòng)的過(guò)程系統(tǒng)允許通過(guò)的 QPS 曲線如下圖所示:
如下圖所示结笨,我們最后想要設(shè)置的值是單機(jī)閾值是10包晰,但是不想讓他一下子到10,而是想讓他慢慢的增長(zhǎng)炕吸,一開(kāi)始的單機(jī)閾值設(shè)置為10(單機(jī)閾值)/3(冷加載因子)=3.3杜窄,5秒(預(yù)熱時(shí)長(zhǎng))之后達(dá)到理想單機(jī)閾值10。
公式:默認(rèn) coldFactor 為 3算途,即請(qǐng)求 QPS 從 threshold / 3 開(kāi)始塞耕,經(jīng)預(yù)熱時(shí)長(zhǎng)逐漸升至設(shè)定的 QPS 閾值。
5嘴瓤、QPS勻速排隊(duì)
勻速排隊(duì)
勻速排隊(duì)(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
)方式會(huì)嚴(yán)格控制請(qǐng)求通過(guò)的間隔時(shí)間扫外,也即是讓請(qǐng)求以均勻的速度通過(guò),對(duì)應(yīng)的是漏桶算法廓脆。詳細(xì)文檔可以參考 流量控制 - 勻速器模式筛谚,具體的例子可以參見(jiàn) PaceFlowDemo。
該方式的作用如下圖所示:
這種方式主要用于處理間隔性突發(fā)的流量停忿,例如消息隊(duì)列驾讲。想象一下這樣的場(chǎng)景,在某一秒有大量的請(qǐng)求到來(lái),而接下來(lái)的幾秒則處于空閑狀態(tài)吮铭,我們希望系統(tǒng)能夠在接下來(lái)的空閑期間逐漸處理這些請(qǐng)求时迫,而不是在第一秒直接拒絕多余的請(qǐng)求。
如上配置表示不管有多少的請(qǐng)求放進(jìn)來(lái)谓晌,一秒鐘只會(huì)處理一個(gè)請(qǐng)求掠拳,超時(shí)時(shí)間設(shè)置為了20s,表示如果這個(gè)請(qǐng)求在20s之后還沒(méi)有被處理就會(huì)報(bào)錯(cuò)纸肉。
六溺欧、Sentinel的服務(wù)降級(jí)
1、簡(jiǎn)介
Sentinel熔斷降級(jí)會(huì)在調(diào)用鏈路中某個(gè)資源出現(xiàn)不穩(wěn)定狀態(tài)時(shí)(例如調(diào)用超時(shí)或異常比例升高)柏肪,對(duì)這個(gè)資源的調(diào)用進(jìn)行限制姐刁,讓請(qǐng)求快速的失敗,當(dāng)資源被降級(jí)時(shí)烦味,在接下來(lái)的降級(jí)時(shí)間窗口之內(nèi)聂使,對(duì)該資源的調(diào)用都自動(dòng)熔斷,而Hystrix則會(huì)有一個(gè)半開(kāi)狀態(tài)拐叉,這是不一樣的岩遗。
RT扇商、異常比例凤瘦、異常數(shù)
2、RT
平均響應(yīng)時(shí)間 (DEGRADE_GRADE_RT):當(dāng) 1s 內(nèi)持續(xù)進(jìn)入 N 個(gè)請(qǐng)求案铺,對(duì)應(yīng)時(shí)刻的平均響應(yīng)時(shí)間(秒級(jí))均超過(guò)閾值(count蔬芥,以 ms 為單位)
,那么在接下的時(shí)間窗口(DegradeRule 中的 timeWindow控汉,以 s 為單位)之內(nèi)笔诵,對(duì)這個(gè)方法的調(diào)用都會(huì)自動(dòng)地熔斷(拋出 DegradeException)。注意 Sentinel 默認(rèn)統(tǒng)計(jì)的 RT 上限是 4900 ms姑子,超出此閾值的都會(huì)算作 4900 ms乎婿,若需要變更此上限可以通過(guò)啟動(dòng)配置項(xiàng) -Dcsp.sentinel.statistic.max.rt=xxx 來(lái)配置。
如上圖所示街佑,設(shè)置RT降級(jí)規(guī)則谢翎,RT設(shè)置為200毫秒,時(shí)間窗口設(shè)置為1s沐旨,也就是說(shuō)森逮,如果訪問(wèn)/testB資源進(jìn)入的N個(gè)請(qǐng)求,訪問(wèn)響應(yīng)的時(shí)間都超過(guò)200ms了磁携,那么在這個(gè)時(shí)間窗口期1s內(nèi)褒侧,就會(huì)發(fā)生服務(wù)熔斷。
3、異常比例
異常比例 (DEGRADE_GRADE_EXCEPTION_RATIO):當(dāng)資源的每秒請(qǐng)求量 >= N(可配置)闷供,并且每秒異逞萄耄總數(shù)占通過(guò)量的比值超過(guò)閾值(DegradeRule 中的 count)之后,資源進(jìn)入降級(jí)狀態(tài)这吻,即在接下的時(shí)間窗口(DegradeRule 中的 timeWindow吊档,以 s 為單位)之內(nèi),對(duì)這個(gè)方法的調(diào)用都會(huì)自動(dòng)地返回唾糯。異常比率的閾值范圍是 [0.0, 1.0]怠硼,代表 0% - 100%。
如上圖所示移怯,如果一秒鐘內(nèi)有四個(gè)請(qǐng)求訪問(wèn)/testB香璃,50%以上的請(qǐng)求,比如說(shuō)3個(gè)請(qǐng)求訪問(wèn)發(fā)生錯(cuò)誤舟误,就會(huì)在1s的時(shí)間窗口內(nèi)發(fā)生服務(wù)熔斷葡秒。
4、異常數(shù)
異常數(shù) (DEGRADE_GRADE_EXCEPTION_COUNT):當(dāng)資源近 1 分鐘的異常數(shù)目超過(guò)閾值之后會(huì)進(jìn)行熔斷嵌溢。注意由于統(tǒng)計(jì)時(shí)間窗口是分鐘級(jí)別的眯牧,若 timeWindow(時(shí)間窗口) 小于 60s,則結(jié)束熔斷狀態(tài)后仍可能再進(jìn)入熔斷狀態(tài)赖草。所以時(shí)間窗口一定要大于等于一分鐘時(shí)間
如上圖所示学少,如果在一分鐘內(nèi),異常數(shù)超過(guò)10個(gè)秧骑,就會(huì)發(fā)生服務(wù)降級(jí)版确。注意時(shí)間窗口一定要大于60s。
七乎折、熱點(diǎn)參數(shù)限流
何為熱點(diǎn)绒疗?熱點(diǎn)即經(jīng)常訪問(wèn)的數(shù)據(jù)。很多時(shí)候我們希望統(tǒng)計(jì)某個(gè)熱點(diǎn)數(shù)據(jù)中訪問(wèn)頻次最高的 Top K 數(shù)據(jù)骂澄,并對(duì)其訪問(wèn)進(jìn)行限制吓蘑。比如:
商品 ID 為參數(shù),統(tǒng)計(jì)一段時(shí)間內(nèi)最常購(gòu)買(mǎi)的商品 ID 并進(jìn)行限制
用戶 ID 為參數(shù)坟冲,針對(duì)一段時(shí)間內(nèi)頻繁訪問(wèn)的用戶 ID 進(jìn)行限制
熱點(diǎn)參數(shù)限流會(huì)統(tǒng)計(jì)傳入?yún)?shù)中的熱點(diǎn)參數(shù)磨镶,并根據(jù)配置的限流閾值與模式,對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用進(jìn)行限流樱衷。熱點(diǎn)參數(shù)限流可以看做是一種特殊的流量控制棋嘲,僅對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效。
1矩桂、在服務(wù)類新加配置
@GetMapping("/testHotKey")
@SentinelResource(value = "testHotKey", blockHandler = "deal_testHotKey")
public String testHotKey(@RequestParam(value = "p1", required = false) String p1,
@RequestParam(value = "p2", required = false) String p2) {
return "-----testHotKey";
}
public String deal_testHotKey(String p1, String p2, BlockedException e) {
return "------deal_testHotKey failed";
}
2沸移、瀏覽器訪問(wèn)
此時(shí)如果訪問(wèn)這個(gè)路徑http://localhost:8401/testHotKey?p1=a痪伦,因?yàn)樯蠄D進(jìn)行了熱點(diǎn)限流,參數(shù)索引是0雹锣,也就是p1网沾,單機(jī)閾值為1,也就是1s內(nèi)訪問(wèn)超過(guò)1次就會(huì)觸發(fā)熱點(diǎn)限流蕊爵,而這里就是觸發(fā)我們寫(xiě)的兜底方法辉哥。如果沒(méi)有兜底方法的話就會(huì)是一個(gè)很不友好的error界面。而且只要有p1就會(huì)進(jìn)行熱點(diǎn)限流
http://localhost:8401/testHotKey?p1=a會(huì)進(jìn)行熱點(diǎn)限流
http://localhost:8401/testHotKey?p1=a&p2=b會(huì)進(jìn)行熱點(diǎn)限流
http://localhost:8401/testHotKey?p2=b不會(huì)進(jìn)行熱點(diǎn)限流
3攒射、參數(shù)例外項(xiàng)(VIP)
如上圖所示醋旦,進(jìn)行了參數(shù)例外項(xiàng)的配置,其實(shí)意思就是當(dāng)索引0下標(biāo)對(duì)應(yīng)的值也就是p1会放,當(dāng)他是5時(shí)饲齐,閾值QPS可以達(dá)到200在進(jìn)行熱點(diǎn)限流,而不再是1咧最。相當(dāng)于是給p1=5開(kāi)了一個(gè)vip捂人。參數(shù)類型必須是八大基本類型和String。
4矢沿、系統(tǒng)保護(hù)規(guī)則
系統(tǒng)保護(hù)規(guī)則是從應(yīng)用級(jí)別的入口流量進(jìn)行控制滥搭,從單臺(tái)機(jī)器的 load、CPU 使用率捣鲸、平均 RT瑟匆、入口 QPS 和并發(fā)線程數(shù)等幾個(gè)維度監(jiān)控應(yīng)用指標(biāo),讓系統(tǒng)盡可能跑在最大吞吐量的同時(shí)保證系統(tǒng)整體的穩(wěn)定性摄狱。系統(tǒng)保護(hù)規(guī)則是應(yīng)用整體維度的脓诡,而不是資源維度的无午,并且僅對(duì)入口流量生效媒役。入口流量指的是進(jìn)入應(yīng)用的流量。
Load 自適應(yīng)(僅對(duì) Linux/Unix-like 機(jī)器生效):
系統(tǒng)的 load1 作為啟發(fā)指標(biāo)宪迟,進(jìn)行自適應(yīng)系統(tǒng)保護(hù)酣衷。當(dāng)系統(tǒng) load1 超過(guò)設(shè)定的啟發(fā)值,且系統(tǒng)當(dāng)前的并發(fā)線程數(shù)超過(guò)估算的系統(tǒng)容量時(shí)才會(huì)觸發(fā)系統(tǒng)保護(hù)(BBR 階段)次泽。系統(tǒng)容量由系統(tǒng)的 maxQps * minRt 估算得出穿仪。設(shè)定參考值一般是 CPU cores * 2.5。
CPU usage(1.5.0+ 版本):
當(dāng)系統(tǒng) CPU 使用率超過(guò)閾值即觸發(fā)系統(tǒng)保護(hù)(取值范圍 0.0-1.0)意荤,比較靈敏啊片。
平均 RT:
當(dāng)單臺(tái)機(jī)器上所有入口流量的平均 RT 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù),單位是毫秒玖像。
并發(fā)線程數(shù):
當(dāng)單臺(tái)機(jī)器上所有入口流量的并發(fā)線程數(shù)達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)紫谷。
入口 QPS:
當(dāng)單臺(tái)機(jī)器上所有入口流量的 QPS 達(dá)到閾值即觸發(fā)系統(tǒng)保護(hù)。
八、@SentinelResource注解
@GetMapping(value = "/byResource")
@SentinelResource(value = "byResource", blockHandler = "handlerException")
public String CommonResult() {
return "success";
}
public String handlerException(BlockException e) {
return "failed" + e;
}
如上圖所示笤昨,如果每一個(gè)方法都要配置一個(gè)blockHandler祖驱,那么會(huì)造成業(yè)務(wù)和代碼高耦合,且造成代碼冗余瞒窒,所以捺僻,把處理方法提取出來(lái)放到一個(gè)類里面,并且可以在這個(gè)類里面配置多個(gè)方法崇裁。
@RestController
public class RateLimitController {
@GetMapping(value = "/extract")
@SentinelResource(value = "extract", blockHandlerClass = CustomerBlockHandler.class, blockHandler = "Handler1")
public String HandlerExtract() {
return "我成功了";
}
}
public class CustomerBlockHandler {
public static String Handler1(BlockException e) {
return "handler1由我來(lái)處理";
}
public static String Handler2(BlockException e) {
return "handler2由我來(lái)處理";
}
}
如上圖所示匕坯,這樣就可以避免了代碼的冗余和業(yè)務(wù)的耦合。并且能夠?qū)崿F(xiàn)全局的統(tǒng)一處理方法拔稳,
@SentinelResource注解其他注意:
注意不可以用private注解
注意Sentinel的三個(gè)核心API:
①:sphU定義資源
②:Tracer定義統(tǒng)計(jì)
③:contextUtil定義上下文
九醒颖、服務(wù)熔斷功能
一、環(huán)境預(yù)說(shuō)
9003和9004只有端口號(hào)的差異泞歉,舉例9003配置:
1、module
2匿辩、pom
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
3腰耙、yml
server:
port: 9003
spring:
application:
name: nacos-payment-provider
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
management:
endpoints:
web:
exposure:
include: '*'
4、主啟動(dòng)
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain9003 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain9003.class, args);
}
}
5铲球、業(yè)務(wù)類
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
public static HashMap<Long, Payment> hashMap = new HashMap<>();
static {
hashMap.put(1L, new Payment(1L, "dfadfhasdfgjadsf"));
hashMap.put(2L, new Payment(2L, "nnnnnnnn"));
hashMap.put(3L, new Payment(3L, "mmmmmmm"));
}
@GetMapping(value = "/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
Payment payment = hashMap.get(id);
CommonResult<Payment> result = new CommonResult<>(200, "serverPort:" + serverPort, payment);
return result;
}
}
8402客戶端環(huán)境:
1挺庞、module
2、pom
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
3稼病、yml
server:
port: 8402
spring:
application:
name: nacos-order-consumer
cloud:
nacos:
discovery:
server-addr: localhost:8848 #配置Nacos地址
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默認(rèn)8719端口选侨,假如被占用會(huì)自動(dòng)從8719+1依次掃描
port: 8719
#消費(fèi)者將要去訪問(wèn)的微服務(wù)名稱(注冊(cè)成功進(jìn)nacos的微服務(wù)提供者)
service-url:
nacos-user-service: http://nacos-payment-provider
4、主啟動(dòng)
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain {
public static void main(String[] args) {
SpringApplication.run(OrderMain.class, args);
}
}
5然走、配置類
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
6援制、業(yè)務(wù)類
@RestController
@Slf4j
public class CircleBreakerController {
public static final String SERVICE_URL = "http://nacos-payment-provider";
@Resource
private RestTemplate restTemplate;
@GetMapping(value = "/consumer/fallback/{id}")
public CommonResult<Payment> fallBack(@PathVariable("id") Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("非法參數(shù)異常");
} else if (result.getData() == null) {
throw new NullPointerException("沒(méi)有對(duì)應(yīng)的記錄");
}
return result;
}
}
如上所示,客戶端在訪問(wèn)時(shí)可以實(shí)現(xiàn)輪詢
二芍瑞、然后由業(yè)務(wù)類來(lái)進(jìn)行服務(wù)熔斷的學(xué)習(xí)
運(yùn)用fallback管理程序運(yùn)行時(shí)異常晨仑,如下所示添加一個(gè)fallback方法,就不再是難看的errorpage頁(yè)面拆檬,而是我們自定義的頁(yè)面洪己。
@GetMapping(value = "/consumer/fallback/{id}")
@SentinelResource(value = "fallback",fallback = "handFallback")
public CommonResult<Payment> fallBack(@PathVariable("id") Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("非法參數(shù)異常");
} else if (result.getData() == null) {
throw new NullPointerException("沒(méi)有對(duì)應(yīng)的記錄");
}
return result;
}
public CommonResult<Payment> handFallback(@PathVariable("id") Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底異常" + e.getMessage(), payment);
}
三、fallback只會(huì)管java產(chǎn)生的異常竟贯,blockHandler只會(huì)管Sentinel控制臺(tái)的配置違規(guī)
如下這樣答捕,既配置blockhandler又配置fallback就可以既處理java異常又處理Sentinel配置違規(guī),如果兩個(gè)規(guī)則都違規(guī)時(shí)屑那,則會(huì)進(jìn)入blockHandler的處理
拱镐。
@GetMapping(value = "/consumer/fallback/{id}")
// @SentinelResource(value = "fallback",fallback = "handFallback")
@SentinelResource(value = "fallback",fallback = "handFallback",blockHandler = "blockHandler")
public CommonResult<Payment> fallBack(@PathVariable("id") Long id) {
CommonResult<Payment> result = restTemplate.getForObject(SERVICE_URL + "/paymentSQL/" + id, CommonResult.class, id);
if (id == 4) {
throw new IllegalArgumentException("非法參數(shù)異常");
} else if (result.getData() == null) {
throw new NullPointerException("沒(méi)有對(duì)應(yīng)的記錄");
}
return result;
}
public CommonResult<Payment> handFallback(@PathVariable("id") Long id, Throwable e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(444, "兜底異常" + e.getMessage(), payment);
}
public CommonResult<Payment> blockHandler(@PathVariable("id") Long id, BlockException e) {
Payment payment = new Payment(id, "null");
return new CommonResult<>(445, "blockhandler異常" + e.getMessage(), payment);
}
exceptionsToIgnore表示忽略這個(gè)異常晌缘,保證這個(gè)程序先走通,如下所示痢站,這里是個(gè)數(shù)組磷箕,可以加多個(gè)異常VIP
四、Sentinel和OpenFeign
1阵难、修改pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2岳枷、修改yml
#添加激活Sentinel對(duì)Feign支持
feign:
sentinel:
enabled: true
3、主啟動(dòng)類添加注解@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderMain8402 {
public static void main(String[] args) {
SpringApplication.run(OrderMain8402.class, args);
}
}
4呜叫、業(yè)務(wù)類
首先是一個(gè)專門(mén)調(diào)用服務(wù)端方法的接口空繁,詳細(xì)看之前OpenFeign的學(xué)習(xí),F(xiàn)eignClient的value對(duì)應(yīng)著服務(wù)類的實(shí)例名朱庆,fallback表示處理降級(jí)的類
@FeignClient(value = "nacos-payment-provider", fallback = PaymentServiceImpl.class)
public interface PaymentService {
@GetMapping(value = "/paymentSQL/{id}")
CommonResult<Payment> paymentSQL(@PathVariable("id") Long id);
}
@Component
public class PaymentServiceImpl implements PaymentService {
@Override
public CommonResult<Payment> paymentSQL(Long id) {
return new CommonResult<>(44444, "服務(wù)降級(jí)返回", new Payment(id, "err"));
}
}
然后在controller就可以直接調(diào)用了盛泡,而不再需要RestTemplate了
@Resource
private PaymentService paymentService;
@GetMapping(value = "consumer/paymentSQL/{id}")
public CommonResult<Payment> paymentSQL(@PathVariable("id") Long id) {
return paymentService.paymentSQL(id);
}
5、測(cè)試
將服務(wù)端停掉
十娱颊、Sentinel的持久化規(guī)則
痛點(diǎn):每次重啟服務(wù)器傲诵,Sentinel的配置就都沒(méi)有了,導(dǎo)致了每次都要重新配置
解決方法:將限流規(guī)則配置到nacos上面箱硕,只要刷新8401某個(gè)rest地址拴竹,Sentinel的流控規(guī)則就能看到,只要Nacos不刪除配置剧罩,就一直有效栓拜。
詳細(xì)步驟:8401示例
1、pom添加依賴
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
2惠昔、修改yml(yml一定要格式正確幕与,因?yàn)楦袷讲徽_,我的Sentinel持久化一直沒(méi)生效)
server:
port: 8401
spring:
application:
name: cloudalibaba-sentinel-service
cloud:
nacos:
discovery:
#Nacos服務(wù)注冊(cè)中心地址
server-addr: localhost:8848
sentinel:
transport:
#配置Sentinel dashboard地址
dashboard: localhost:8080
#默認(rèn)8719端口镇防,假如被占用會(huì)自動(dòng)從8719+1依次掃描
port: 8719
datasource:
ds1:
nacos:
server-addr: localhost:8848
dataId: cloudalibaba-sentinel-service
groupId: DEFAULT_GROUP
data_type: json
rule-type: flow
management:
endpoints:
web:
exposure:
include: '*'
在yml中配置的dataId即對(duì)應(yīng)著在如下Nacos中配置的Data ID啦鸣。
3、在nacos中配置Sentinel流控規(guī)則
配置規(guī)則:
resource:
資源名营罢,即限流規(guī)則的作用對(duì)象赏陵;
limitApp:
流控針對(duì)的調(diào)用來(lái)源饼齿,若為 default 則不區(qū)分調(diào)用來(lái)源饲漾;
grade:
限流閾值類型: 0表示線程數(shù),1表示QPS缕溉;
strategy:
流控模式考传,0表示直接,1表示關(guān)聯(lián)证鸥,2表示鏈路僚楞;
controlBehavior:
流量控制效果:0快速失敗勤晚、1Warm Up、2排隊(duì)等待泉褐;
clusterMode:
是否集群赐写。
這時(shí)Sentinel中就會(huì)一直有我們配置的限流規(guī)則了,但是目前來(lái)看Sentinel的持久化配置比較復(fù)雜膜赃,應(yīng)該是個(gè)半成品挺邀,期待阿里后期繼續(xù)完善。