Sentinel概述
在基于Spring Cloud構(gòu)建的微服務體系中半抱,服務之間的調(diào)用鏈路會隨著系統(tǒng)的演進變得越來越長檐薯,這無疑會增加了整個系統(tǒng)的不可靠因素材原。在并發(fā)流量比較高的情況下,由于網(wǎng)絡調(diào)用之間存在一定的超時時間袖迎,鏈路中的某個服務出現(xiàn)宕機都會大大增加整個調(diào)用鏈路的響應時間冕臭,而瞬間的流量洪峰則會導致這條鏈路上所有服務的可用線程資源被打滿,從而造成整體服務的不可用燕锥,這也就是我們常說的“雪崩效應”辜贵。
而在微服務系統(tǒng)設計的過程中,為了應對這樣的糟糕情況归形,最常用的手段就是進行”流量控制“以及對網(wǎng)絡服務的調(diào)用實現(xiàn)“熔斷降級”托慨。所謂流量控制就是根據(jù)服務的承載能力制定一個策略,將一定時間窗口內(nèi)的網(wǎng)絡調(diào)用次數(shù)進行限制暇榴,例如1s內(nèi)某個服務最多只能處理10個請求厚棵,那么1s內(nèi)的第11+的請求會被被限制丟棄;而熔斷降級的概念則是說在A服務→B服務調(diào)用過程中蔼紧,按照一定的規(guī)則A服務發(fā)現(xiàn)調(diào)用B服務經(jīng)常失敗婆硬,如果觸發(fā)了A服務對B服務調(diào)用的熔斷降級規(guī)則,那么在一定時間窗口內(nèi)奸例,A服務在處理請求的過程中對于B服務的調(diào)用將會直接在A服務的邏輯中被熔斷降級彬犯,請求則不會通過網(wǎng)絡打到B服務,從而避免A服務由于過長的超時時間導致自身資源被耗盡的情況發(fā)生查吊。
雖然我們知道以上兩種手段非常有用谐区,如果沒有合適的技術(shù)來支持,就好像一句話說的“雖然明白很多道理菩貌,但是依然過不好這一生”一樣。而Sentinel就是這樣一種技術(shù)重荠,它是阿里巴巴開源的一款客戶端限流組件箭阶,可以與Spring Cloud微服務體系無縫地集成;而與之對應的是另外一款Netflix公司推出的知名度也比較高的Hystrix組件戈鲁,Hystrix也是Spring Cloud官方集成熔斷限流組件仇参,只不過相對于Sentinel來說,Hystrix所提供的功能和靈活度比較低婆殿,并且它目前已經(jīng)處于開源版本暫停維護的狀態(tài)诈乒,因此目前國內(nèi)很多基于Spring Cloud搞微服務的公司都轉(zhuǎn)向了Sentinel。關(guān)于二者的對比由于不是本文的重點婆芦,這里就不再贅述怕磨,大家搜索下就好(ps:可能網(wǎng)上也沒幾篇能說明白的文章喂饥,關(guān)鍵還在于大家實際使用對比)。
Sentinel+Apollo架構(gòu)說明
Sentinel開源版本架構(gòu)
在Github Sentinel官方Wiki說明以及網(wǎng)上一大堆的水文中肠鲫,關(guān)于Sentinel的資料已經(jīng)很多了员帮,但是大多數(shù)屬于Demo級別,所以本文不想過多的耗費大家的精力(因為在學習過程中导饲,作者也被誤導過)捞高。以下將從實際生產(chǎn)的使用方式上來闡述如何構(gòu)建Sentinel的使用架構(gòu)。
從本質(zhì)上說Sentinel與Hystrix是一類性質(zhì)的熔斷限流組件渣锦,之所以說它們只是組件就在于它們都需要內(nèi)嵌于微服務應用本身的主進程之中硝岗,所有的限流、熔斷策略及指標信息的收集等邏輯都是基于客戶端的(這里不要對客戶端有所誤會袋毙,它指的是處于調(diào)用端上游的微服務本身)型檀。而這一點是明顯區(qū)別于Service Mesh(服務網(wǎng)格)架構(gòu)中將熔斷、限流等邏輯抽象在SideCar(邊車)而不是微服務應用本身的娄猫。
因此從這種意義上說贱除,Sentinel的使用應該是并不復雜的,它應該與Hystrix一樣媳溺,在Spring Cloud微服務應用中引入相關(guān)依賴即可月幌。事實上從某種程度來說的確如此,只不過Sentinel提供了比Hystrix要強一點的規(guī)則配置能力悬蔽,提供了可以進行限流扯躺、熔斷降級以及熱點、授權(quán)等其他規(guī)則統(tǒng)一配置和管理的控制臺服務->sentinel-dashboard蝎困。
雖然如此录语,但這也并沒有改變Sentinel作為客戶端限流組件性質(zhì),通過控制臺配置的規(guī)則依然要推送到微服務應用Sentinel客戶端本身才能生效禾乘,而微服務之間的調(diào)用鏈路等指標信息也需要推送給Sentinel控制臺澎埠,才能比較方便地使用Sentinel提供的一些能力,因此在開源的架構(gòu)版本中需要微服務應用本身開啟獨立端口與sentinel-dashboard進行通信始藕,從而獲取配置規(guī)則以及上送微服務應用各類指標信息蒲稳。而這一點,顯然也會占用微服務額外的資源伍派,并且由于sentinel-dashboard在此條件下并不具備集群部署能力江耀,因此也會形成一個單節(jié)點問題,但是有一套控制臺總好過于沒有诉植,如果希望比較方便快速地應用Sentinel這也是一種代價祥国。
此時的Sentinel架構(gòu)如下圖所示:
Sentinel+Apollo架構(gòu)
在開源版本架構(gòu)中,通過sentinel-dashboard控制臺配置的限流晾腔、熔斷降級等規(guī)則都是存儲于Sentinel控制臺服務內(nèi)存之中的舌稀,如果控制臺服務重啟或者微服務應用重啟都會導致規(guī)則丟失啊犬。而這在生產(chǎn)環(huán)境下是不可接受的,因此Sentinel在官方的生產(chǎn)架構(gòu)指導中也是推薦使用第三方數(shù)據(jù)源(如本文的Apollo)作為永久存儲中心扩借,這樣各個微服務的限流椒惨、降級規(guī)則都可以永久存儲。雖然Sentinel官方推薦使用第三方數(shù)據(jù)源作為規(guī)則存儲中心潮罪,目前也提供了針對Apollo康谆、Nacos、Zookeeper嫉到、Redis沃暗、Consul、Spring Cloud Config等多種存儲源的依賴集成Jar何恶,但是卻并沒有針對這些數(shù)據(jù)源提供一個可以實際使用的sentinel-dashboard第三方數(shù)據(jù)源存儲版本孽锥,所以當你選擇了一種數(shù)據(jù)源那么就需要你自己對sentinel-dashboard項目進行改造,這里作者針對Sentinel 1.7.0(成文時最新版本)使用Apollo數(shù)據(jù)源改造了一個版本细层,所有規(guī)則基本可用惜辑,但可能會有細節(jié)的Bug需要自行Fix。具體代碼改造點見Github鏈接:
https://github.com/manongwudi/Sentinel/commit/f3a27adb6fdbf13d9eaa4510e317c1b55c206e89
關(guān)于以上sentinel-dashboard接入Apollo數(shù)據(jù)源的代碼改造情況疫赎,大家可以詳細參考上述鏈接盛撑,這里作者只說以下幾個重點:
1)、目前官方推薦的方式是通過Apollo的開放平臺授權(quán)的方式進行寫入捧搞,因此我們需要在sentinel-dashboard項目pom.xml文件引入以下依賴:
<!-- Apollo配置依賴 -->
<dependency>
????<groupId>com.ctrip.framework.apollo</groupId>
????<artifactId>apollo-openapi</artifactId>
????<version>1.5.0</version>
</dependency>
2)抵卫、之后我們需要在Apollo Portal創(chuàng)建一個針對sentinel-dashboard的應用,具體創(chuàng)建方法如下圖所示:
以上我們創(chuàng)建了一個針對Sentinel控制臺的應用(這里的應用是Apollo配置中心的基本概念胎撇,具體微服務接入Apollo的方法介粘,大家可以自行搜索)。
3)晚树、創(chuàng)建應用后姻采,未來Sentinel控制臺在啟動是需要指定Apollo應用ID才能接入Apollo,而接入Apollo之后Sentinel的規(guī)則需要寫入該應用下的namespace空間爵憎,因此還需要創(chuàng)建針對該應用的namespace空間慨亲,具體創(chuàng)建方式如下圖所示:
點擊進入應用,然后點擊“添加Namespace",創(chuàng)建一個具體存儲Sentinel各種限流纲堵、熔斷降級等規(guī)則的Apollo存儲空間巡雨,這里需要注意的是所創(chuàng)建的空間類型一定要是"public"公共空間闰渔,因為最終這些規(guī)則是需要具體的微服務應用去獲取的席函,而在Apollo中應用下只有公共Namecspace才能被其他應用繼承。
4)冈涧、最后我們在Apollo控制臺選擇“管理員工具->開放平臺授權(quán)管理”創(chuàng)建基于該應用的開放授權(quán)信息茂附。
此時生成的Token信息將作為sentinel-dashboard與Apollo接口對接的重要憑證被配置正蛙。通過上述幾個步驟,我們基本上就完成了sentinel-dashboard對接Apollo的準備工作营曼,剩下的就是針對sentinel-dashboard的具體代碼改造乒验,可參考前面的Github鏈接。改造中能夠抽離的配置如下:
#Apollo本地演示環(huán)境
#Apollo應用ID
apollo.app.id=sentinel
#Apollo應用下對應的具體集群標識
apollo.cluster.name=local
#Apollo存儲空間名稱
apollo.namespace.name=sentinel-rule
#Apollo控制臺地址
apollo.portal.url=http://127.0.0.1:8070
#Apollo控制臺用戶名
apollo.modify.user=apollo
apollo.release.user=apollo
#Apollo開放平臺憑證
apollo.application.token=2647efacc9d55445f4055247cd028af60dd604b6
以上配置在編寫具體的連接代碼時會使用到蒂阱,詳情請參考具體改造代碼锻全!
為什么要使用Apollo?Apollo是一款攜程開源的配置中心录煤,在目前基于Spring Cloud的微服務體系中也有一款官方的配置中心Spring Cloud Config鳄厌。從實際的使用情況看,目前Apollo比起Spring Cloud Config從功能上說要更全一些妈踊,如果你的公司在使用Spring Cloud Config那么可以參考上述代碼對sentinel-dashboard自行進行改造了嚎,只是由于作者所做公司目前使用的是Apollo作為配置中心,因此選擇的是Apollo作為Sentinel第三方存儲數(shù)據(jù)源(需要注意Apollo的版本廊营,如果你所使用的Apollo版本比較老歪泳,可能會不兼容)。
引入Apollo作為Sentinel數(shù)據(jù)存儲源后露筒,此時的Sentinel架構(gòu)如下圖所示:
Spring Cloud微服務集成Sentinel
講到這里呐伞,我們還只是完成了Sentinel控制臺與Apollo數(shù)據(jù)存儲源之間的打通,那么對于具體的Spring Cloud微服務應用而言邀窃,在代碼編程上該如何接入和使用Sentinel呢荸哟?
微服務連接Sentinel控制臺
在默認情況下微服務應用可以直接連接Sentinel控制臺,從而通過Sentinel控制臺獲取限流瞬捕、熔斷降級等規(guī)則信息鞍历。具體步驟如下:
1)、首先我們需要在項目pom.xml文件中引入Sentinel相關(guān)依賴Jar肪虎,代碼如下:
<!--Sentinel熔斷限流組件依賴-->
<dependency>
????<groupId>com.alibaba.cloud</groupId>
????<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
????<version>2.1.1.RELEASE</version>
????<!--根據(jù)實際情況決定是否排除沖突依賴-->
????<exclusions>
????????<exclusion>
????????????<groupId>com.alibaba</groupId>
????????????<artifactId>fastjson</artifactId>
????????</exclusion>
????</exclusions>
</dependency>
2)劣砍、之后我們需要在項目的配置文件中加入Spring Cloud微服務連接sentinel dashboard的配置(此時微服務還尚未引入Apollo配置中心,引入Apollo配置中心也可以加在配置中心)扇救,如下:
#sentinel
#在微服務應用中開啟連接sentinel-dashboard的連接端口
spring.cloud.sentinel.transport.port =?8719
#sentinel-dashboard控制臺地址
spring.cloud.sentinel.transport.dashboard = http://127.0.0.1:9090
在不考慮第三方數(shù)據(jù)源永久存儲的情況下刑枝,以上方式也可以直接使用Sentinel對微服務進行限流、熔斷降級等邏輯迅腔,只不過這些規(guī)則并不能永久存儲装畅!
微服務連接Apollo配置中心
接下來我們將Spring Cloud微服務接入Apollo配置中心,并通過Apollo配置中心獲取從Sentinel控制臺持久化到Apollo應用存儲空間的Sentinel規(guī)則沧烈。
引入Sentinel規(guī)則Apollo數(shù)據(jù)源依賴掠兄,該依賴也會默認包含Apollo本身的客戶端依賴,因此也不用在額外引入其他JAR,代碼如下:
<!--Sentinel規(guī)則Apollo數(shù)據(jù)源依賴-->
<dependency>
????<groupId>com.alibaba.csp</groupId>
????<artifactId>sentinel-datasource-apollo</artifactId>
????<version>1.7.0</version>
????<!--根據(jù)實際情況決定是否排除沖突依賴-->
????<exclusions>
????????<exclusion>
????????????<groupId>com.alibaba.csp</groupId>
????????????<artifactId>sentinel-datasource-extension</artifactId>
????????</exclusion>
????????<exclusion>
????????????<groupId>com.alibaba.csp</groupId>
????????????<artifactId>sentinel-core</artifactId>
????????</exclusion>
????</exclusions>
</dependency>
接下來配置Spring Cloud微服務連接Apollo配置中心的基本信息蚂夕,如下:
#Apollo中創(chuàng)建的微服務應用ID
app.id = pay-notify
#打開Apollo接入開關(guān)
apollo.bootstrap.enabled =?true
apollo.bootstrap.eagerLoad.enabled =?true
#apollo configserver地址不是portal
apollo.meta = http://127.0.0.1:8080
# 自定義本地配置文件緩存路徑
apollo.cacheDir = ./config
#指定apollo命名空間
apollo.namespace =application,db,logback
#指定apollo集群
apollo.cluster=local
如果希望在Apollo中生效的配置能夠及時被Spring Cloud微服務感知到迅诬,我們還需要在微服務主類中加入@EnableApolloConfig注解,代碼如下:
@SpringBootApplication
@EnableFeignClients(basePackageClasses = {PayChannelFeignService.class})
@EnableDiscoveryClient
@EnableTransactionManagement
@EnableApolloConfig
public?class?PayNotifyApplication {
????public?static?void?main(String[] args) {
????????SpringApplication.run(PayNotifyApplication.class, args);
????}
????// 注解支持的配置Bean
????@Bean
????public?SentinelResourceAspect sentinelResourceAspect() {
????????return?new?SentinelResourceAspect();
????}
}
此時拋開Sentinel本身不說婿牍,Spring Cloud微服務也可以通過Apollo進行配置管理了侈贷!
那么嵌入Spring Cloud微服務應用的Sentitle客戶端該如何獲取Apollo中關(guān)于Sentinel規(guī)則的配置呢?在文章前面關(guān)于Sentinel+Apollo架構(gòu)的說明中等脂,
sentinel-dashboard是將規(guī)則寫入它在Apollo所在應用的公共空間下尔苦,因此其他微服務本身是可以通過Apollo繼承并讀取到這些配置的议街。只是我們在進行sentinel-dashboard的改造將規(guī)則的寫入編成了一定的前/后綴
標示,所以Spring Cloud微服務要想匹配到相應的規(guī)則,也需要在自身服務的配置中約定讀取方式芬膝,具體以限流聪蘸、熔斷降級這兩個規(guī)則為例進行配置译暂,如下:
#指定該數(shù)據(jù)源為限流規(guī)則
spring.cloud.sentinel.datasource.flow.apollo.rule-type = flow
spring.cloud.sentinel.datasource.degrade.apollo.rule-type = degrade
#指定該規(guī)則在apollo應用中的key,從而實現(xiàn)約定讀取
#spring.cloud.sentinel.datasource.ds1.apollo.flow-rules-key = ${spring.application.name}-${spring.cloud.sentinel.datasource.flow.apollo.rule-type}
spring.cloud.sentinel.datasource.flow.apollo.namespaceName = sentinel-rule
spring.cloud.sentinel.datasource.flow.apollo.flowRulesKey = ${spring.application.name}-${spring.cloud.sentinel.datasource.flow.apollo.rule-type}
#降級規(guī)則
spring.cloud.sentinel.datasource.degrade.apollo.namespaceName = sentinel-rule
spring.cloud.sentinel.datasource.degrade.apollo.flowRulesKey = ${spring.application.name}-${spring.cloud.sentinel.datasource.degrade.apollo.rule-type}
通過上述配置可以看出乍惊,我們是通過Sentinel客戶端依賴約定的配置方式,對各類規(guī)則通過命名規(guī)則進行了匹配(這里Sentinel規(guī)則的命名規(guī)則可以結(jié)合實際的管理需求進行約定解幼,確保sentinel-dashboard寫入與微服務讀取匹配就行)抑党!例如:如果從管理角度分類,可以加上{部門名稱}.sentinel-rule撵摆,這要求創(chuàng)建namespace公共空間時帶上部門名前綴底靠。
微服務使用Sentinel的編程方式
通過上面操作,我們已經(jīng)從配置及環(huán)境方面完成了Sentinel與Spring Cloud微服務的接入特铝,接下來我們以實際的服務間調(diào)用為例演示如何在Spring Cloud微服務體系下暑中,使用Sentinel進行限流、熔斷降級等操作鲫剿!以作者之前做過的支付系統(tǒng)為例鳄逾,其中有兩個微服務存在如下調(diào)用關(guān)系:
以上兩個服務的調(diào)用示例,是在支付系統(tǒng)中對支付訂單狀態(tài)進行實時檢查的邏輯灵莲,目的是防止在出現(xiàn)支付調(diào)用鏈路中斷雕凹,導致的支付掉單問題。pay-check服務會在支付請求發(fā)送到第三方后接受一條延遲消息政冻,并在一定時間后通過對比支付流水狀態(tài)與第三方渠道支付狀態(tài)枚抵,如發(fā)現(xiàn)狀態(tài)不一致,會通過Spring Cloud微服務間的Feign調(diào)用方式觸發(fā)支付通知服務pay-notify明场,從而實現(xiàn)支付鏈路的補償汽摹。這里pay-notify提供的服務接口為“/internel/pay/checkNotify”,關(guān)于這個服務接口苦锨,這里我們要求pay-notify服務要對該資源進行限流逼泣,從而防止流量過大而導致正常的通知鏈路受影響嫌套;而對于pay-check服務則需要實現(xiàn)對pay-notify服務該接口資源的熔斷降級邏輯防止由于故障或網(wǎng)絡原因?qū)е聀ay-notify服務無法被正常調(diào)用,從而影響pay-check服務的穩(wěn)定性圾旨。
Sentinel限流編程
需要明確的是,限流動作本身是服務提供方做出的魏蔗,所以如果需要針對某個微服務的應用接口使用Sentinel進行限流處理砍的,那么我們可以在該服務的入口通過@SentinelResource注解進行Sentinel資源配置,以上述實例為例莺治,我們對pay-notify微服務接口/internel/pay/checkNotify進行如下資源定義:
@SentinelResource(value =?"/pay/checkNotify", blockHandlerClass = SentinelFallback.class, blockHandler =?"fallbackHandlerForCheckNotify")
@RequestMapping(value =?"/pay/checkNotify", method = RequestMethod.POST)
public?boolean?checkNotify(@RequestParam(value =?"paymentId") String paymentId,
????????@RequestParam(value =?"tradeNo") String tradeNo,?@RequestParam(value =?"status")?int?status,
????????@RequestParam(value =?"platform") String platform,?@RequestParam(value =?"platformtag") String platformtag,
????????@RequestParam(value =?"tradeTime") String tradeTime,
????????@RequestParam(value =?"notifyOrignMsg") String notifyOrignMsg,
????????@RequestParam(value =?"tradeStatus") String tradeStatus) {
????PayCoreNotifyEntity payCoreNotifyEntity = PayCoreNotifyEntity.builder().paymentId(paymentId).tradeNo(tradeNo)
????????????.status(status).platform(platform).platformtag(platformtag).tradeTime(tradeTime)
????????????.orignPlaintext(notifyOrignMsg).tradeStatus(tradeStatus).build();
????boolean?result =?false;
????try?{
????????result = payCheckNotifyServiceImpl.payCheckCallBack(payCoreNotifyEntity);
????}?catch?(CheckNotifyMsgException e) {
????????log.error(e.toString() +?"_"?+ e.getMessage(), e);
????????CounterUtil.counter(Arrays.asList(Tag.of("exceptionType", e.getMessage())),?"payNotifyExceptionMonitor");
????}
????return?result;
}
在針對該接口進行Sentinel資源的定義時廓鞠,為了服務端不直接拋出BlockException異常,我們配置了異常處理類及異常處理方法谣旁。blockHandler函數(shù)會在原資源方法被限流系統(tǒng)保護時被調(diào)用床佳,而在SentinelFallback類中,針對該資源方法也定義了相應的地處理方法fallbackHandlerForCheckNotify榄审,代碼如下:
@Slf4j
public?class?SentinelFallback {
????public?static?boolean?fallbackHandlerForCheckNotify(String paymentId,
????????????@RequestParam(value =?"tradeNo") String tradeNo,?@RequestParam(value =?"status")?int?status,
????????????@RequestParam(value =?"platform") String platform,?@RequestParam(value =?"platformtag") String platformtag,
????????????@RequestParam(value =?"tradeTime") String tradeTime,
????????????@RequestParam(value =?"notifyOrignMsg") String notifyOrignMsg,
????????????@RequestParam(value =?"tradeStatus") String tradeStatus, BlockException e) {
????????log.error("對不起砌们,該請求限流了,{}", e.toString());
????????return?false;
????}
需要注意的是Block異常處理函數(shù),參數(shù)最后多一個BlockException搁进,其余參數(shù)則需要與原函數(shù)一致浪感,否則限流規(guī)則觸發(fā)后將無法正常進入該fallback方法,而是直接拋出異常饼问,服務消費方則直接收到500錯誤影兽,輸出上會顯得不是很友好!關(guān)于限流資源異常處理代碼編程方式莱革,以上只是參考峻堰,大家可以寫的更優(yōu)雅,例如可以參考Feign的fallback編程方式盅视。
定義Sentinel資源后捐名,此時如果需要針對該資源進行限流規(guī)則的配置,我們可以使用sentinel-dashboard進行配置闹击,如圖所示:
在pay-notify微服務節(jié)點上桐筏,選擇流控規(guī)則,并按照前面定義的Sentinel資源名稱進行限流規(guī)則配置拇砰,這里我們?yōu)榱吮阌跍y試梅忌,限流規(guī)則配置的極端些,選擇QPS方式除破,并定義閥值為0牧氮。此時由于已經(jīng)將sentinel-dashbord與Apollo配置中心打通,因此也能從Apollo中看到已經(jīng)持久化存儲的限流規(guī)則瑰枫,如下圖所示:
此時如果針對該資源方法進行網(wǎng)絡調(diào)用就會被Sentinel規(guī)則限流掉踱葛,例如通過postman對/internel/pay/checkNotify接口進行網(wǎng)絡調(diào)用丹莲,服務端代碼運行如下:
可以看到此時針對該方法的調(diào)用已經(jīng)觸發(fā)限流規(guī)則,并在拋出BlockException異常后尸诽,進入了我們前面通過@SentinelResource注解定義的blockHandler方法甥材!
Sentinel熔斷降級編程
熔斷降級針對的是對其他服務資源進行網(wǎng)絡調(diào)用時,為了防止外部服務的不穩(wěn)定拖垮自身性含,當該服務出現(xiàn)不穩(wěn)定狀態(tài)(例如調(diào)用超時或者異常比例升高等情況)洲赵,對該資源的調(diào)用動作進行限制,從而讓請求快速失敗商蕴,避免出現(xiàn)級聯(lián)錯誤的情況叠萍。而當資源被降級后,在接下來的降級時間窗口內(nèi)绪商,對該資源的服務調(diào)用都會自動熔斷苛谷,而不會真正進行網(wǎng)絡調(diào)用,而在Sentinel中則默認會拋出DegradeException異常格郁。
從使用方向上看熔斷降級規(guī)則邏輯的發(fā)生腹殿,是發(fā)生在服務消費方,而不是服務提供方例书。以上述例子舉例赫蛇,pay-notify服務除了針對自身接口進行限流外,pay-check對pay-notify服務的調(diào)用也可以進行熔斷降級處理雾叭。這里我們將pay-check服務中對pay-notify服務接口的調(diào)用方法進行Sentinel資源定義悟耘,代碼如下:
@SentinelResource(value =?"/pay/goCheckNotify", blockHandler =?"testNotifyFallback")
public?boolean?testNotify(String paymentId, String tradeNo,?int?status, String platform, String platformtag,
????????String tradeTime, String notifyOrignMsg, String tradeStatus)?throws?BlockException {
????return?notifyClient
????????????.checkNotify(paymentId, tradeNo, status, platform, platformtag, tradeTime, notifyOrignMsg, tradeStatus);
}
//熔斷降級異常處理方法
public?boolean?testNotifyFallback(String paymentId, String tradeNo,?int?status, String platform, String platformtag,
????????String tradeTime, String notifyOrignMsg, String tradeStatus, BlockException ex) {
????log.error("服務被降級了!");
????//todo 調(diào)用其他降級服務
????return?false;
}
之后我們通過Sentinel控制臺在微服務節(jié)點pay-check中針對該資源配置降級規(guī)則织狐,如下圖所示:
這里的意思是對該資源方法的調(diào)用按照平均響應時間進行熔斷降級暂幼,當1S內(nèi)持續(xù)進入5個請求,對應時刻的資源平均響應時間(秒級)均超過閥值移迫,這里配置的是200ms旺嬉,那么在接下來的時間窗口內(nèi),這里配置的是10s厨埋,對該資源的調(diào)用都會自動進行熔斷邪媳,默認拋出DegradeException。
為了演示效果荡陷,這里我們將pay-notify服務的接口響應時間故意sleep(600ms)雨效,代碼如下:
try?{
????Thread.sleep(600);
}?catch?(InterruptedException e) {
????e.printStackTrace();
}
接下來我們通過JMeter調(diào)用pay-check服務,而pay-check服務將以Spring Cloud微服務的調(diào)用方式通過Feign來調(diào)用pay-notify的服務废赞,如下圖所示:
這里我們設置的請求方式為1s內(nèi)發(fā)送7次請求調(diào)用徽龟,按照熔斷降級規(guī)則,由于前5個請求的響應時間都將超過200ms的閥值唉地,因此第6据悔、7個請求將被直接熔斷而進入fallback方法传透。代碼運行效果如下:
從演示效果看熔斷降級規(guī)則已經(jīng)生效,Sentinel拋出了DegradeException異常极颓!
Sentinel與Feign的集成關(guān)系
在實際的Spring Cloud微服務開發(fā)中朱盐,微服務之間的調(diào)用可以通過Feign來實現(xiàn),與Spring Cloud微服務官方集成的Hystrix框架一樣菠隆,在Feign中如果需要開啟Sentinel熔斷降級邏輯兵琳,需要在調(diào)用端(示例中為pay-check服務)配置中進行如下配置:
feign.sentinel.enabled =?true
而作為Spring Cloud微服務調(diào)用端,在基于Feign對其他微服務存在接口調(diào)用的話浸赫,一般情況下我們還需要編寫基于Feign的調(diào)用代碼,并指定其fallback邏輯赃绊,以本文示例為例:
@FeignClient(value =?"pay-notify", configuration = PayNotifyClientConfiguration.class, fallbackFactory = PayNotifyClientFallbackFactory.class)
public?interface?PayNotifyClient {
????/**
?????* 支付狀態(tài)核對發(fā)生掉單現(xiàn)象時既峡,通過此接口完成補單回調(diào)操作
?????*
?????* @param paymentId
?????* @param tradeNo
?????* @param status
?????* @param platform
?????* @param platformtag
?????* @param tradeTime
?????* @param notifyOrignMsg
?????* @param tradeStatus
?????* @return
?????*/
????@RequestMapping(value =?"/internal/pay/checkNotify", method = RequestMethod.POST)
????boolean?checkNotify(@RequestParam(value =?"paymentId") String paymentId,
????????????@RequestParam(value =?"tradeNo") String tradeNo,?@RequestParam(value =?"status")?int?status,
????????????@RequestParam(value =?"platform") String platform,?@RequestParam(value =?"platformtag") String platformtag,
????????????@RequestParam(value =?"tradeTime") String tradeTime,
????????????@RequestParam(value =?"notifyOrignMsg") String notifyOrignMsg,
????????????@RequestParam(value =?"tradeStatus") String tradeStatus);
}
其所指定的Fallback邏輯代碼如下:
@Slf4j
public?class?PayNotifyClientFallbackFactory?implements?FallbackFactory<PayNotifyClient> {
????@Override
????public?PayNotifyClient create(Throwable throwable) {
????????return?new?PayNotifyClient() {
????????????@Override
????????????public?boolean?checkNotify(String paymentId, String tradeNo,?int?status, String platform,
????????????????????String platformtag, String tradeTime, String notifyOrignMsg, String tradeStatus) {
????????????????log.info("enter flow limit/fallback logic");
????????????????log.error(throwable.getMessage());
????????????????return?false;
????????????}
????????};
????}
}
@Slf4j
@Configuration
public?class?PayNotifyClientConfiguration {
????@Bean
????PayNotifyClientFallbackFactory payNotifyClientFallbackFactory() {
????????return?new?PayNotifyClientFallbackFactory();
????}
}
后記
關(guān)于Sentinel還有一些其他規(guī)則功能,如授權(quán)碧查、熱點規(guī)則运敢、集群限流等高級功能!而由于篇幅的關(guān)系忠售,這里暫時只介紹單機限流传惠、熔斷降級這兩種最常用的功能!