SpringCloud中Sentinel架構(gòu)的使用方法

Sentinel架構(gòu)怎么用嗎?一般大家對(duì)Spring Cloud中Sentinel架構(gòu)的了解可能停留在概念的層面上,而對(duì)于Sentinel架構(gòu)的使用方法了解相對(duì)較少。今天就跟大家聊聊Sentinel架構(gòu)的使用方法。

創(chuàng)新互聯(lián)公司始終堅(jiān)持【策劃先行,效果至上】的經(jīng)營(yíng)理念,通過多達(dá)十余年累計(jì)超上千家客戶的網(wǎng)站建設(shè)總結(jié)了一套系統(tǒng)有效的全網(wǎng)整合營(yíng)銷推廣解決方案,現(xiàn)已廣泛運(yùn)用于各行各業(yè)的客戶,其中包括:建筑動(dòng)畫等企業(yè),備受客戶贊譽(yù)。

一、Sentinel概述

在基于Spring Cloud構(gòu)建的微服務(wù)體系中,服務(wù)之間的調(diào)用鏈路會(huì)隨著系統(tǒng)的演進(jìn)變得越來越長(zhǎng),這無疑會(huì)增加了整個(gè)系統(tǒng)的不可靠因素。在并發(fā)流量比較高的情況下,由于網(wǎng)絡(luò)調(diào)用之間存在一定的超時(shí)時(shí)間,鏈路中的某個(gè)服務(wù)出現(xiàn)宕機(jī)都會(huì)大大增加整個(gè)調(diào)用鏈路的響應(yīng)時(shí)間,而瞬間的流量洪峰則會(huì)導(dǎo)致這條鏈路上所有服務(wù)的可用線程資源被打滿,從而造成整體服務(wù)的不可用,這也就是我們常說的“雪崩效應(yīng)”。

而在微服務(wù)系統(tǒng)設(shè)計(jì)的過程中,為了應(yīng)對(duì)這樣的糟糕情況,最常用的手段就是進(jìn)行”流量控制“以及對(duì)網(wǎng)絡(luò)服務(wù)的調(diào)用實(shí)現(xiàn)“熔斷降級(jí)”。所謂流量控制就是根據(jù)服務(wù)的承載能力指定一個(gè)策略,對(duì)一定時(shí)間窗口內(nèi)的網(wǎng)絡(luò)調(diào)用次數(shù)進(jìn)行限制,例如1s內(nèi)某個(gè)服務(wù)最多只能處理10個(gè)請(qǐng)求,那么1s內(nèi)的第11+的請(qǐng)求會(huì)被被限制丟棄;而熔斷降級(jí)的概念則是說在A服務(wù)→B服務(wù)調(diào)用過程中,按照一定的規(guī)則A服務(wù)發(fā)現(xiàn)調(diào)用B服務(wù)經(jīng)常失敗或響應(yīng)時(shí)間過長(zhǎng),如果觸發(fā)了A服務(wù)對(duì)B服務(wù)調(diào)用的熔斷降級(jí)規(guī)則,那么在一定時(shí)間窗口內(nèi),A服務(wù)在處理請(qǐng)求的過程中對(duì)于B服務(wù)的調(diào)用將會(huì)直接在A服務(wù)的邏輯中被熔斷降級(jí),請(qǐng)求則不會(huì)通過網(wǎng)絡(luò)打到B服務(wù),從而避免A服務(wù)由于過長(zhǎng)的超時(shí)時(shí)間導(dǎo)致自身資源被耗盡的情況發(fā)生。

雖然我們知道以上兩種手段非常有用,但若沒有合適的技術(shù)來支持,就好像一句話說的“雖然明白很多道理,但是依然過不好這一生”一樣。而Sentinel就是這樣一種技術(shù),它是阿里巴巴開源的一款客戶端限流組件,可以與Spring Cloud微服務(wù)體系無縫地集成;而與之對(duì)應(yīng)的是另外一款Netflix公司推出的知名度也比較高的Hystrix組件,Hystrix也是Spring Cloud官方集成熔斷限流組件,只不過相對(duì)于Sentinel來說,Hystrix所提供的功能和靈活度比較低,并且它目前已經(jīng)處于開源版本暫停維護(hù)的狀態(tài),因此目前國(guó)內(nèi)很多基于Spring Cloud搞微服務(wù)的公司都轉(zhuǎn)向了Sentinel。關(guān)于二者的對(duì)比由于不是本文的重點(diǎn),這里就不再贅述,大家搜索下就好(ps:可能網(wǎng)上也沒幾篇能說明白的文章,關(guān)鍵還在于大家實(shí)際使用對(duì)比)。

二、Sentinel+Apollo架構(gòu)說明

Sentinel開源版本架構(gòu)

在Github Sentinel官方Wiki說明以及網(wǎng)上一大堆的水文中,關(guān)于Sentinel的資料已經(jīng)很多了,但是大多數(shù)屬于Demo級(jí)別,所以本文不想過多的耗費(fèi)大家的精力(因?yàn)樵趯W(xué)習(xí)過程中,作者也被誤導(dǎo)過)。以下將從實(shí)際生產(chǎn)的使用方式上來闡述如何構(gòu)建Sentinel的使用架構(gòu)。

從本質(zhì)上說Sentinel與Hystrix是一類性質(zhì)的熔斷限流組件,之所以說它們只是組件就在于它們都需要內(nèi)嵌于微服務(wù)應(yīng)用本身的主進(jìn)程之中,所有的限流、熔斷策略及指標(biāo)信息的收集等邏輯都是基于客戶端的(這里不要對(duì)客戶端有所誤會(huì),它指的是處于調(diào)用端上游的微服務(wù)本身)。而這一點(diǎn)是明顯區(qū)別于Service Mesh(服務(wù)網(wǎng)格)架構(gòu)中將熔斷、限流等邏輯抽象在SideCar(邊車)而不是微服務(wù)應(yīng)用本身的。

因此從這種意義上說,Sentinel的使用應(yīng)該是并不復(fù)雜的,它應(yīng)該與Hystrix一樣,在Spring Cloud微服務(wù)應(yīng)用中引入相關(guān)依賴即可。事實(shí)上從某種程度來說的確如此,只不過Sentinel提供了比Hystrix要強(qiáng)一點(diǎn)的規(guī)則配置能力,提供了可以進(jìn)行限流、熔斷降級(jí)以及熱點(diǎn)、授權(quán)等其他規(guī)則統(tǒng)一配置和管理的控制臺(tái)服務(wù)->sentinel-dashboard。

雖然如此,但這也并沒有改變Sentinel作為客戶端限流組件性質(zhì),通過控制臺(tái)配置的規(guī)則依然要推送到微服務(wù)應(yīng)用Sentinel客戶端本身才能生效,而微服務(wù)之間的調(diào)用鏈路等指標(biāo)信息也需要推送給Sentinel控制臺(tái),才能比較方便地使用Sentinel提供的一些能力,因此在開源的架構(gòu)版本中需要微服務(wù)應(yīng)用本身開啟獨(dú)立端口與sentinel-dashboard進(jìn)行通信,從而獲取配置規(guī)則以及上送微服務(wù)應(yīng)用各類指標(biāo)信息。而這一點(diǎn),顯然也會(huì)占用微服務(wù)額外的資源,并且由于sentinel-dashboard在此條件下并不具備集群部署能力,因此也會(huì)形成一個(gè)單節(jié)點(diǎn)問題,但是有一套控制臺(tái)總好過于沒有,如果希望比較方便快速地應(yīng)用Sentinel這也是一種代價(jià)。此時(shí)的Sentinel架構(gòu)如下圖所示:

在開源版本架構(gòu)中,通過sentinel-dashboard控制臺(tái)配置的限流、熔斷降級(jí)等規(guī)則都是存儲(chǔ)于Sentinel控制臺(tái)服務(wù)內(nèi)存之中的,如果控制臺(tái)服務(wù)重啟或者微服務(wù)應(yīng)用重啟都會(huì)導(dǎo)致規(guī)則丟失。而這在生產(chǎn)環(huán)境下是不可接受的,因此Sentinel在官方的生產(chǎn)架構(gòu)指導(dǎo)中也是推薦使用第三方數(shù)據(jù)源(如本文的Apollo)作為永久存儲(chǔ)中心,這樣各個(gè)微服務(wù)的限流、降級(jí)規(guī)則都可以永久存儲(chǔ)。雖然Sentinel官方推薦使用第三方數(shù)據(jù)源作為規(guī)則存儲(chǔ)中心,目前也提供了針對(duì)Apollo、Nacos、Zookeeper、redis、Consul、Spring Cloud Config等多種存儲(chǔ)源的依賴集成Jar,但是卻并沒有針對(duì)這些數(shù)據(jù)源提供一個(gè)可以實(shí)際使用的sentinel-dashboard第三方數(shù)據(jù)源存儲(chǔ)版本,所以當(dāng)你選擇了一種數(shù)據(jù)源那么就需要你自己對(duì)sentinel-dashboard項(xiàng)目進(jìn)行改造,這里作者針對(duì)Sentinel 1.7.0(成文時(shí)最新版本)使用Apollo數(shù)據(jù)源改造了一個(gè)版本,所有規(guī)則基本可用,但可能會(huì)有細(xì)節(jié)的Bug需要自行Fix。具體代碼改造點(diǎn)見Github鏈接:

https://github.com/manongwudi/Sentinel/commit/f3a27adb6fdbf13d9eaa4510e317c1b55c206e89

關(guān)于以上sentinel-dashboard接入Apollo數(shù)據(jù)源的代碼改造情況,大家可以詳細(xì)參考上述鏈接,這里作者只說以下幾個(gè)重點(diǎn):

目前官方推薦的方式是通過Apollo的開放平臺(tái)授權(quán)的方式進(jìn)行寫入,因此我們需要在sentinel-dashboard項(xiàng)目pom.xml文件引入以下依賴:

<!-- Apollo配置依賴 -->
<dependency>
    <groupId>com.ctrip.framework.apollo</groupId>
    <artifactId>apollo-openapi</artifactId>
    <version>1.5.0</version>
</dependency>

之后我們需要在Apollo Portal創(chuàng)建一個(gè)針對(duì)sentinel-dashboard的應(yīng)用

以上我們創(chuàng)建了一個(gè)針對(duì)Sentinel控制臺(tái)的應(yīng)用(這里的應(yīng)用是Apollo配置中心的基本概念,具體微服務(wù)接入Apollo的方法,大家可以自行搜索)。

創(chuàng)建應(yīng)用后,未來Sentinel控制臺(tái)在啟動(dòng)是需要指定Apollo應(yīng)用ID才能接入Apollo,而接入Apollo之后Sentinel的規(guī)則需要寫入該應(yīng)用下的namespace空間,因此還需要?jiǎng)?chuàng)建針對(duì)該應(yīng)用的namespace空間

點(diǎn)擊進(jìn)入應(yīng)用,然后點(diǎn)擊“添加Namespace",創(chuàng)建一個(gè)具體存儲(chǔ)Sentinel各種限流、熔斷降級(jí)等規(guī)則的Apollo存儲(chǔ)空間,這里需要注意的是所創(chuàng)建的空間類型一定要是"public"公共空間,因?yàn)樽罱K這些規(guī)則是需要具體的微服務(wù)應(yīng)用去獲取的,而在Apollo中應(yīng)用下只有公共Namecspace才能被其他應(yīng)用繼承。

最后我們?cè)贏pollo控制臺(tái)選擇“管理員工具->開放平臺(tái)授權(quán)管理”創(chuàng)建基于該應(yīng)用的開放授權(quán)信息。

此時(shí)生成的Token信息將作為sentinel-dashboard與Apollo接口對(duì)接的重要憑證被配置。通過上述幾個(gè)步驟,我們基本上就完成了sentinel-dashboard對(duì)接Apollo的準(zhǔn)備工作,剩下的就是針對(duì)sentinel-dashboard的具體代碼改造,可參考前面的Github鏈接。改造中能夠抽離的配置如下:

#Apollo本地演示環(huán)境
#Apollo應(yīng)用ID
apollo.app.id=sentinel
#Apollo應(yīng)用下對(duì)應(yīng)的具體集群標(biāo)識(shí)
apollo.cluster.name=local
#Apollo存儲(chǔ)空間名稱
apollo.namespace.name=sentinel-rule
#Apollo控制臺(tái)地址
apollo.portal.url=http://127.0.0.1:8070
#Apollo控制臺(tái)用戶名
apollo.modify.user=apollo
apollo.release.user=apollo
#Apollo開放平臺(tái)憑證
apollo.application.token=2647efacc9d55445f4055247cd028af60dd604b6

以上配置在編寫具體的連接代碼時(shí)會(huì)使用到,詳情請(qǐng)參考具體改造代碼!

為什么要使用Apollo?Apollo是一款攜程開源的配置中心,在目前基于Spring Cloud的微服務(wù)體系中也有一款官方的配置中心Spring Cloud Config。從實(shí)際的使用情況看,目前Apollo比起Spring Cloud Config從功能上說要更全一些,如果你的公司在使用Spring Cloud Config司目前使用的是Apollo作為配置中心,因此選擇的是Apollo作為Sentinel第三方存儲(chǔ)數(shù)據(jù)源(需要注意Apollo的版本,如果你所使用的Apollo版本比較老,可能會(huì)不兼容)。

三、Spring Cloud微服務(wù)集成Sentinel

講到這里,我們還只是完成了Sentinel控制臺(tái)與Apollo數(shù)據(jù)存儲(chǔ)源之間的打通,那么對(duì)于具體的Spring Cloud微服務(wù)應(yīng)用而言,在代碼編程上該如何接入和使用Sentinel呢?

微服務(wù)連接Sentinel控制臺(tái)

在默認(rèn)情況下微服務(wù)應(yīng)用可以直接連接Sentinel控制臺(tái),從而通過Sentinel控制臺(tái)獲取限流、熔斷降級(jí)等規(guī)則信息。具體步驟如下:

首先我們需要在項(xiàng)目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ù)實(shí)際情況決定是否排除沖突依賴-->
    <exclusions>
        <exclusion>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </exclusion>
    </exclusions>
</dependency>

之后我們需要在項(xiàng)目的配置文件中加入Spring Cloud微服務(wù)連接sentinel dashboard的配置(此時(shí)微服務(wù)還尚未引入Apollo配置中心,引入Apollo配置中心后也可以加在配置中心),如下:

#sentinel
#在微服務(wù)應(yīng)用中開啟連接sentinel-dashboard的連接端口
spring.cloud.sentinel.transport.port = 8719
#sentinel-dashboard控制臺(tái)地址
spring.cloud.sentinel.transport.dashboard = http://127.0.0.1:9090

在不考慮第三方數(shù)據(jù)源永久存儲(chǔ)的情況下,以上方式也可以直接使用Sentinel對(duì)微服務(wù)進(jìn)行限流、熔斷降級(jí)等邏輯,只不過這些規(guī)則并不能永久存儲(chǔ)!

微服務(wù)連接Apollo配置中心

接下來我們將Spring Cloud微服務(wù)接入Apollo配置中心,并通過Apollo配置中心獲取從Sentinel控制臺(tái)持久化到Apollo應(yīng)用存儲(chǔ)空間的Sentinel規(guī)則。

引入Sentinel規(guī)則Apollo數(shù)據(jù)源依賴,該依賴也會(huì)默認(rèn)包含Apollo本身的客戶端依賴,因此也不用在額外引入其他JAR,代碼如下:

<!--Sentinel規(guī)則Apollo數(shù)據(jù)源依賴-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-apollo</artifactId>
    <version>1.7.0</version>
    <!--根據(jù)實(shí)際情況決定是否排除沖突依賴-->
    <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微服務(wù)連接Apollo配置中心的基本信息,如下:

#Apollo中創(chuàng)建的微服務(wù)應(yī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中生效的配置能夠及時(shí)被Spring Cloud微服務(wù)感知到,我們還需要在微服務(wù)主類中加入@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();
    }
}

此時(shí)拋開Sentinel本身不說,Spring Cloud微服務(wù)也可以通過Apollo進(jìn)行配置管理了!

那么嵌入Spring Cloud微服務(wù)應(yīng)用的Sentitle客戶端該如何獲取Apollo中關(guān)于Sentinel規(guī)則的配置呢?在文章前面關(guān)于Sentinel+Apollo架構(gòu)的說明中,sentinel-dashboard是將規(guī)則寫入它在Apollo所在應(yīng)用的公共空間下,因此其他微服務(wù)本身是可以通過Apollo繼承并讀取到這些配置的。只是我們?cè)谶M(jìn)行sentinel-dashboard的改造將規(guī)則的寫入編成了一定的前/后綴標(biāo)示,所以Spring Cloud微服務(wù)要想匹配到相應(yīng)的規(guī)則,也需要在自身服務(wù)的配置中約定讀取方式,具體以限流、熔斷降級(jí)這兩個(gè)規(guī)則為例進(jìn)行配置,如下:

#指定該數(shù)據(jù)源為限流規(guī)則
spring.cloud.sentinel.datasource.flow.apollo.rule-type = flow
spring.cloud.sentinel.datasource.degrade.apollo.rule-type = degrade

#指定該規(guī)則在apollo應(yīng)用中的key,從而實(shí)現(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}

#降級(jí)規(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客戶端依賴約定的配置方式,對(duì)各類規(guī)則通過命名規(guī)則進(jìn)行了匹配(這里Sentinel規(guī)則的命名規(guī)則可以結(jié)合實(shí)際的管理需求進(jìn)行約定,確保sentinel-dashboard寫入與微服務(wù)讀取匹配就行)!例如:如果從管理角度分類,可以加上{部門名稱}.sentinel-rule,這要求創(chuàng)建namespace公共空間時(shí)帶上部門名前綴。

四、微服務(wù)使用Sentinel的編程方式

通過上面操作,我們已經(jīng)從配置及環(huán)境方面完成了Sentinel與Spring Cloud微服務(wù)的接入,接下來我們以實(shí)際的服務(wù)間調(diào)用為例演示如何在Spring Cloud微服務(wù)體系下,使用Sentinel進(jìn)行限流、熔斷降級(jí)等操作!

以上兩個(gè)服務(wù)的調(diào)用示例,是在支付系統(tǒng)中對(duì)支付訂單狀態(tài)進(jìn)行實(shí)時(shí)檢查的邏輯,目的是防止在出現(xiàn)支付調(diào)用鏈路中斷,導(dǎo)致的支付掉單問題。pay-check服務(wù)會(huì)在支付請(qǐng)求發(fā)送到第三方后接受一條延遲消息,并在一定時(shí)間后通過對(duì)比支付流水狀態(tài)與第三方渠道支付狀態(tài),如發(fā)現(xiàn)狀態(tài)不一致,會(huì)通過Spring Cloud微服務(wù)間的Feign調(diào)用方式觸發(fā)支付通知服務(wù)pay-notify,從而實(shí)現(xiàn)支付鏈路的補(bǔ)償。這里pay-notify提供的服務(wù)接口為“/internel/pay/checkNotify”,關(guān)于這個(gè)服務(wù)接口,這里我們要求pay-notify服務(wù)要對(duì)該資源進(jìn)行限流,從而防止流量過大而導(dǎo)致正常的通知鏈路受影響;而對(duì)于pay-check服務(wù)則需要實(shí)現(xiàn)對(duì)pay-notify服務(wù)該接口資源的熔斷降級(jí)邏輯防止由于故障或網(wǎng)絡(luò)原因?qū)е聀ay-notify服務(wù)無法被正常調(diào)用,從而影響pay-check服務(wù)的穩(wěn)定性。

Sentinel限流編程

需要明確的是,限流動(dòng)作本身是服務(wù)提供方做出的,所以如果需要針對(duì)某個(gè)微服務(wù)的應(yīng)用接口使用Sentinel進(jìn)行限流處理,那么我們可以在該服務(wù)的入口通過@SentinelResource注解進(jìn)行Sentinel資源配置,以上述實(shí)例為例,我們對(duì)pay-notify微服務(wù)接口/internel/pay/checkNotify進(jìn)行如下資源定義:

@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;
}

在針對(duì)該接口進(jìn)行Sentinel資源的定義時(shí),為了服務(wù)端不直接拋出BlockException異常,我們配置了異常處理類及異常處理方法。blockHandler函數(shù)會(huì)在原資源方法被限流系統(tǒng)保護(hù)時(shí)被調(diào)用,而在SentinelFallback類中,針對(duì)該資源方法也定義了相應(yīng)的地處理方法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("對(duì)不起,該請(qǐng)求限流了,{}", e.toString());
        return false;
    }

需要注意的是Block異常處理函數(shù),參數(shù)最后多一個(gè)BlockException,其余參數(shù)則需要與原函數(shù)一致,否則限流規(guī)則觸發(fā)后將無法正常進(jìn)入該fallback方法,而是直接拋出異常,服務(wù)消費(fèi)方則直接收到500錯(cuò)誤,輸出上會(huì)顯得不是很友好!關(guān)于限流資源異常處理代碼編程方式,以上只是參考,大家可以寫的更優(yōu)雅,例如可以參考Feign的fallback編程方式。

定義Sentinel資源后,此時(shí)如果需要針對(duì)該資源進(jìn)行限流規(guī)則的配置,我們可以使用sentinel-dashboard進(jìn)行配置

在pay-notify微服務(wù)節(jié)點(diǎn)上,選擇流控規(guī)則,并按照前面定義的Sentinel資源名稱進(jìn)行限流規(guī)則配置,這里我們?yōu)榱吮阌跍y(cè)試,限流規(guī)則配置的極端些,選擇QPS方式,并定義閥值為0。此時(shí)由于已經(jīng)將sentinel-dashbord與Apollo配置中心打通,因此也能從Apollo中看到已經(jīng)持久化存儲(chǔ)的限流規(guī)則,

此時(shí)如果針對(duì)該資源方法進(jìn)行網(wǎng)絡(luò)調(diào)用就會(huì)被Sentinel規(guī)則限流掉,例如通過postman對(duì)/internel/pay/checkNotify接口進(jìn)行網(wǎng)絡(luò)調(diào)用,

可以看到此時(shí)針對(duì)該方法的調(diào)用已經(jīng)觸發(fā)限流規(guī)則,并在拋出BlockException異常后,進(jìn)入了我們前面通過@SentinelResource注解定義的blockHandler方法!

Sentinel熔斷降級(jí)編程

熔斷降級(jí)針對(duì)的是對(duì)其他服務(wù)資源進(jìn)行網(wǎng)絡(luò)調(diào)用時(shí),為了防止外部服務(wù)的不穩(wěn)定拖垮自身,當(dāng)該服務(wù)出現(xiàn)不穩(wěn)定狀態(tài)(例如調(diào)用超時(shí)或者異常比例升高等情況),對(duì)該資源的調(diào)用動(dòng)作進(jìn)行限制,從而讓請(qǐng)求快速失敗,避免出現(xiàn)級(jí)聯(lián)錯(cuò)誤的情況。而當(dāng)資源被降級(jí)后,在接下來的降級(jí)時(shí)間窗口內(nèi),對(duì)該資源的服務(wù)調(diào)用都會(huì)自動(dòng)熔斷,而不會(huì)真正進(jìn)行網(wǎng)絡(luò)調(diào)用,而在Sentinel中則默認(rèn)會(huì)拋出DegradeException異常。

從使用方向上看熔斷降級(jí)規(guī)則邏輯的發(fā)生,是發(fā)生在服務(wù)消費(fèi)方,而不是服務(wù)提供方。以上述例子舉例,pay-notify服務(wù)除了針對(duì)自身接口進(jìn)行限流外,pay-check對(duì)pay-notify服務(wù)的調(diào)用也可以進(jìn)行熔斷降級(jí)處理。這里我們將pay-check服務(wù)中對(duì)pay-notify服務(wù)接口的調(diào)用方法進(jìn)行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);
}

//熔斷降級(jí)異常處理方法
public boolean testNotifyFallback(String paymentId, String tradeNo, int status, String platform, String platformtag,
        String tradeTime, String notifyOrignMsg, String tradeStatus, BlockException ex) {
    log.error("服務(wù)被降級(jí)了!");
    //todo 調(diào)用其他降級(jí)服務(wù)
    return false;
}

之后我們通過Sentinel控制臺(tái)在微服務(wù)節(jié)點(diǎn)pay-check中針對(duì)該資源配置降級(jí)規(guī)則

這里的意思是對(duì)該資源方法的調(diào)用按照平均響應(yīng)時(shí)間進(jìn)行熔斷降級(jí),當(dāng)1S內(nèi)持續(xù)進(jìn)入5個(gè)請(qǐng)求,對(duì)應(yīng)時(shí)刻的資源平均響應(yīng)時(shí)間(秒級(jí))均超過閥值,這里配置的是200ms,那么在接下來的時(shí)間窗口內(nèi),這里配置的是10s,對(duì)該資源的調(diào)用都會(huì)自動(dòng)進(jìn)行熔斷,默認(rèn)拋出DegradeException。為了演示效果,這里我們將pay-notify服務(wù)的接口響應(yīng)時(shí)間故意sleep(600ms),代碼如下:

try {
    Thread.sleep(600);
} catch (InterruptedException e) {
    e.printStackTrace();
}

接下來我們通過JMeter調(diào)用pay-check服務(wù),而pay-check服務(wù)將以Spring Cloud微服務(wù)的調(diào)用方式通過Feign來調(diào)用pay-notify的服務(wù),

這里我們?cè)O(shè)置的請(qǐng)求方式為1s內(nèi)發(fā)送7次請(qǐng)求調(diào)用,按照熔斷降級(jí)規(guī)則,由于前5個(gè)請(qǐng)求的響應(yīng)時(shí)間都將超過200ms的閥值,因此第6、7個(gè)請(qǐng)求將被直接熔斷而進(jìn)入fallback方法。

從演示效果看熔斷降級(jí)規(guī)則已經(jīng)生效,Sentinel拋出了DegradeException異常!

Sentinel與Feign的集成關(guān)系

在實(shí)際的Spring Cloud微服務(wù)開發(fā)中,微服務(wù)之間的調(diào)用可以通過Feign來實(shí)現(xiàn),與Spring Cloud微服務(wù)官方集成的Hystrix框架一樣,在Feign中如果需要開啟Sentinel熔斷降級(jí)邏輯,需要在調(diào)用端(示例中為pay-check服務(wù))配置中進(jìn)行如下配置:

feign.sentinel.enabled = true

而作為Spring Cloud微服務(wù)調(diào)用端,在基于Feign對(duì)其他微服務(wù)存在接口調(diào)用的話,一般情況下我們還需要編寫基于Feign的調(diào)用代碼,并指定其fallback邏輯,以本文示例為例:

@FeignClient(value = "pay-notify", configuration = PayNotifyClientConfiguration.class, fallbackFactory = PayNotifyClientFallbackFactory.class)
public interface PayNotifyClient {
    /**
     * 支付狀態(tài)核對(duì)發(fā)生掉單現(xiàn)象時(shí),通過此接口完成補(bǔ)單回調(diào)操作
     */
    @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邏輯代碼如下:

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();
    }
}

需要說明的是,在微服務(wù)調(diào)用時(shí),如果發(fā)送調(diào)用超時(shí)等情況會(huì)直接進(jìn)入以上Feign所指定的fallback邏輯;而Sentinel熔斷降級(jí)規(guī)則被觸發(fā)時(shí)在某些場(chǎng)景下,例如在上述以平均響應(yīng)時(shí)間為例的降級(jí)規(guī)則中,則只會(huì)直接進(jìn)入@SentinelResource所指定的fallback方法,這一點(diǎn)也是Sentinel與Feign整合不夠優(yōu)雅的地方,因此在編寫容錯(cuò)代碼時(shí)并不能像Hystrix那樣做到那么優(yōu)雅統(tǒng)一!

看完這篇文章,你們學(xué)會(huì)使用Spring Cloud中的Sentinel架構(gòu)了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

文章標(biāo)題:SpringCloud中Sentinel架構(gòu)的使用方法
網(wǎng)站鏈接:http://bm7419.com/article20/igdgjo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供面包屑導(dǎo)航、搜索引擎優(yōu)化、Google、域名注冊(cè)、網(wǎng)站設(shè)計(jì)、網(wǎng)站導(dǎo)航

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管