主頁 > 後端開發 > Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰總結

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰總結

2020-09-10 10:37:03 後端開發

在Spring Cloud微服務體系中,由于限流熔斷組件Hystrix開源版本不在維護,因此國內不少有類似需求的公司已經將眼光轉向阿里開源的Sentinel框架,而以下要介紹的正是作者最近兩個月的真實專案實踐程序,這中間被不少網路Demo示例級別水文誤導過,為了以正視聽特將實踐程序加以總結,希望能夠幫到有類似需要的朋友!(PS:此文有點長,看下概念部分后可以點擊關注+收藏,以備需要)

一、Sentinel概述

在基于Spring Cloud構建的微服務體系中,服務之間的呼叫鏈路會隨著系統的演進變得越來越長,這無疑會增加了整個系統的不可靠因素,在并發流量比較高的情況下,由于網路呼叫之間存在一定的超時時間,鏈路中的某個服務出現宕機都會大大增加整個呼叫鏈路的回應時間,而瞬間的流量洪峰則會導致這條鏈路上所有服務的可用執行緒資源被打滿,從而造成整體服務的不可用,這也就是我們常說的“雪崩效應”,

而在微服務系統設計的程序中,為了應對這樣的糟糕情況,最常用的手段就是進行”流量控制“以及對網路服務的呼叫實作“熔斷降級”,所謂流量控制就是根據服務的承載能力指定一個策略,對一定時間視窗內的網路呼叫次數進行限制,例如1s內某個服務最多只能處理10個請求,那么1s內的第11+的請求會被被限制丟棄;而熔斷降級的概念則是說在A服務→B服務呼叫程序中,按照一定的規則A服務發現呼叫B服務經常失敗或回應時間過長,如果觸發了A服務對B服務呼叫的熔斷降級規則,那么在一定時間視窗內,A服務在處理請求的程序中對于B服務的呼叫將會直接在A服務的邏輯中被熔斷降級,請求則不會通過網路打到B服務,從而避免A服務由于過長的超時時間導致自身資源被耗盡的情況發生,

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

二、Sentinel+Apollo架構說明

Sentinel開源版本架構

在Github Sentinel官方Wiki說明以及網上一大堆的水文中,關于Sentinel的資料已經很多了,但是大多數屬于Demo級別,所以本文不想過多的耗費大家的精力(因為在學習程序中,作者也被誤導過),以下將從實際生產的使用方式上來闡述如何構建Sentinel的使用架構,

從本質上說Sentinel與Hystrix是一類性質的熔斷限流組件,之所以說它們只是組件就在于它們都需要內嵌于微服務應用本身的主行程之中,所有的限流、熔斷策略及指標資訊的收集等邏輯都是基于客戶端的(這里不要對客戶端有所誤會,它指的是處于呼叫端上游的微服務本身),而這一點是明顯區別于Service Mesh(服務網格)架構中將熔斷、限流等邏輯抽象在SideCar(邊車)而不是微服務應用本身的,

因此從這種意義上說,Sentinel的使用應該是并不復雜的,它應該與Hystrix一樣,在Spring Cloud微服務應用中引入相關依賴即可,事實上從某種程度來說的確如此,只不過Sentinel提供了比Hystrix要強一點的規則配置能力,提供了可以進行限流、熔斷降級以及熱點、授權等其他規則統一配置和管理的控制臺服務->sentinel-dashboard,

雖然如此,但這也并沒有改變Sentinel作為客戶端限流組件性質,通過控制臺配置的規則依然要推送到微服務應用Sentinel客戶端本身才能生效,而微服務之間的呼叫鏈路等指標資訊也需要推送給Sentinel控制臺,才能比較方便地使用Sentinel提供的一些能力,因此在開源的架構版本中需要微服務應用本身開啟獨立埠與sentinel-dashboard進行通信,從而獲取配置規則以及上述微服務應用各類指標資訊,而這一點,顯然也會占用微服務額外的資源,并且由于sentinel-dashboard在此條件下并不具備集群部署能力,因此也會形成一個單節點問題,但是有一套控制臺總好過于沒有,如果希望比較方便快速地應用Sentinel這也是一種代價,此時的Sentinel架構如下圖所示:LIK2

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

Sentinel+Apollo架構

在開源版本架構中,通過sentinel-dashboard控制臺配置的限流、熔斷降級等規則都是存盤于Sentinel控制臺服務記憶體之中的,如果控制臺服務重啟或者微服務應用重啟都會導致規則丟失,而這在生產環境下是不可接受的,因此Sentinel在官方的生產架構指導中也是推薦使用第三方資料源(如本文的Apollo)作為永久存盤中心,這樣各個微服務的限流、降級規則都可以永久存盤,雖然Sentinel官方推薦使用第三方資料源作為規則存盤中心,目前也提供了針對Apollo、Nacos、Zookeeper、Redis、Consul、Spring Cloud Config等多種存盤源的依賴集成Jar,但是卻并沒有針對這些資料源提供一個可以實際使用的sentinel-dashboard第三方資料源存盤版本,所以當你選擇了一種資料源那么就需要你自己對sentinel-dashboard專案進行改造,這里作者針對Sentinel 1.7.0(成文時最新版本)使用Apollo資料源改造了一個版本,所有規則基本可用,但可能會有細節的Bug需要自行Fix,具體代碼改造點見Github鏈接:LIK3

關于以上sentinel-dashboard接入Apollo資料源的代碼改造情況,大家可以詳細參考上述鏈接,這里作者只說以下幾個重點:

目前官方推薦的方式是通過Apollo的開放平臺授權的方式進行寫入,因此我們需要在sentinel-dashboard專案pom.xml檔案引入以下依賴:

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

之后我們需要在Apollo Portal創建一個針對sentinel-dashboard的應用,具體創建方法如下圖所示:LIK4

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

以上我們創建了一個針對Sentinel控制臺的應用(這里的應用是Apollo配置中心的基本概念,具體微服務接入Apollo的方法,大家可以自行搜索),

創建應用后,未來Sentinel控制臺在啟動時需要指定Apollo應用ID才能接入Apollo,而接入Apollo之后Sentinel的規則需要寫入該應用下的namespace空間,因此還需要創建針對該應用的namespace空間,具體創建方式如下圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

點擊進入應用,然后點擊“添加Namespace",創建一個具體存盤Sentinel各種限流、熔斷降級等規則的Apollo存盤空間,這里需要注意的是所創建的空間型別一定要是"public"公共空間,因為最終這些規則是需要具體的微服務應用去獲取的,而在Apollo中應用下只有公共Namecspace才能被其他應用繼承,
最后我們在Apollo控制臺選擇“管理員工具->開放平臺授權管理”創建基于該應用的開放授權資訊,

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

此時生成的Token資訊將作為sentinel-dashboard與Apollo介面對接的重要憑證被配置,通過上述幾個步驟,我們基本上就完成了sentinel-dashboard對接Apollo的準備作業,剩下的就是針對sentinel-dashboard的具體代碼改造,可參考前面的Github鏈接,改造中能夠抽離的配置如下:LIK5

#Apollo本地演示環境
#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第三方存盤資料源(需要注意Apollo的版本,如果你所使用的Apollo版本比較老,可能會不兼容),

引入Apollo作為Sentinel資料存盤源后,此時的Sentinel架構如下圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

三、Spring Cloud微服務集成Sentinel

講到這里,我們還只是完成了Sentinel控制臺與Apollo資料存盤源之間的打通,那么對于具體的Spring Cloud微服務應用而言,在代碼編程上該如何接入和使用Sentinel呢?


微服務連接Sentinel控制臺

在默認情況下微服務應用可以直接連接Sentinel控制臺,從而通過Sentinel控制臺獲取限流、熔斷降級等規則資訊,具體步驟如下:

首先我們需要在專案pom.xml檔案中引入Sentinel相關依賴Jar,代碼如下:

<!--Sentinel熔斷限流組件依賴-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    <version>2.1.1.RELEASE</version>
    <!--根據實際情況決定是否排除沖突依賴-->
    <exclusions>
        <exclusion>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </exclusion>
    </exclusions>
</dependency>

之后我們需要在專案的組態檔中加入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

在不考慮第三方資料源永久存盤的情況下,以上方式也可以直接使用Sentinel對微服務進行限流、熔斷降級等邏輯,只不過這些規則并不能永久存盤!

微服務連接Apollo配置中心

接下來我們將Spring Cloud微服務接入Apollo配置中心,并通過Apollo配置中心獲取從Sentinel控制臺持久化到Apollo應用存盤空間的Sentinel規則,

引入Sentinel規則Apollo資料源依賴,該依賴也會默認包含Apollo本身的客戶端依賴,因此也不用再額外引入其他JAR,代碼如下:

<!--Sentinel規則Apollo資料源依賴-->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-apollo</artifactId>
    <version>1.7.0</version>
    <!--根據實際情況決定是否排除沖突依賴-->
    <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中創建的微服務應用ID
app.id = pay-notify
#打開Apollo接入開關
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中關于Sentinel規則的配置呢?在文章前面關于Sentinel+Apollo架構的說明中,sentinel-dashboard是將規則寫入它在Apollo所在應用的公共空間下,因此其他微服務本身是可以通過Apollo繼承并讀取到這些配置的,只是我們在進行sentinel-dashboard的改造將規則的寫入編成了一定的前/后綴標示,所以Spring Cloud微服務要想匹配到相應的規則,也需要在自身服務的配置中約定讀取方式,具體以限流、熔斷降級這兩個規則為例進行配置,如下:

#指定該資料源為限流規則
spring.cloud.sentinel.datasource.flow.apollo.rule-type = flow
spring.cloud.sentinel.datasource.degrade.apollo.rule-type = degrade

#指定該規則在apollo應用中的key,從而實作約定讀取
#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}

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

四、微服務使用Sentinel的編程方式

通過上面操作,我們已經從配置及環境方面完成了Sentinel與Spring Cloud微服務的接入,接下來我們以實際的服務間呼叫為例演示如何在Spring Cloud微服務體系下,使用Sentinel進行限流、熔斷降級等操作!以作者之前做過的支付系統為例,其中有兩個微服務存在如下呼叫關系:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

以上兩個服務的呼叫示例,是在支付系統中對支付訂單狀態進行實時檢查的邏輯,目的是防止在出現支付呼叫鏈路中斷,導致的支付掉單問題,pay-check服務會在支付請求發送到第三方后接受一條延遲訊息,并在一定時間后通過對比支付流水狀態與第三方渠道支付狀態,如發現狀態不一致,會通過Spring Cloud微服務間的Feign呼叫方式觸發支付通知服務pay-notify,從而實作支付鏈路的補償,這里pay-notify提供的服務介面為“/internel/pay/checkNotify”,關于這個服務介面,這里我們要求pay-notify服務要對該資源進行限流,從而防止流量過大而導致正常的通知鏈路受影響;而對于pay-check服務則需要實作對pay-notify服務該介面資源的熔斷降級邏輯防止由于故障或網路原因導致pay-notify服務無法被正常呼叫,從而影響pay-check服務的穩定性,

Sentinel限流編程

需要明確的是,限流動作本身是服務提供方做出的,所以如果需要針對某個微服務的應用介面使用Sentinel進行限流處理,那么我們可以在該服務的入口通過@SentinelResource注解進行Sentinel資源配置,以上述實體為例,我們對pay-notify微服務介面/internel/pay/checkNotify進行如下資源定義:

@SentinelResource(value = "https://www.cnblogs.com/pay/checkNotify", blockHandlerClass = SentinelFallback.class, blockHandler = "fallbackHandlerForCheckNotify")
@RequestMapping(value = "https://www.cnblogs.com/pay/checkNotify", method = RequestMethod.POST)
public boolean checkNotify(@RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/paymentId") String paymentId,
        @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeNo") String tradeNo, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/status") int status,
        @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platform") String platform, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platformtag") String platformtag,
        @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeTime") String tradeTime,
        @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/notifyOrignMsg") String notifyOrignMsg,
        @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/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函式會在原資源方法被限流系統保護時被呼叫,而在SentinelFallback類中,針對該資源方法也定義了相應的地處理方法fallbackHandlerForCheckNotify,代碼如下:

@Slf4j
public class SentinelFallback {

    public static boolean fallbackHandlerForCheckNotify(String paymentId,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeNo") String tradeNo, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/status") int status,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platform") String platform, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platformtag") String platformtag,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeTime") String tradeTime,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/notifyOrignMsg") String notifyOrignMsg,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeStatus") String tradeStatus, BlockException e) {
        log.error("對不起,該請求限流了,{}", e.toString());
        return false;
    }

需要注意的是Block例外處理函式,引數最后多一個BlockException,其余引數則需要與原函式一致,否則限流規則觸發后將無法正常進入該fallback方法,而是直接拋出例外,服務消費方則直接收到500錯誤,輸出上會顯得不是很友好!關于限流資源例外處理代碼編程方式,以上只是參考,大家可以寫的更優雅,例如可以參考Feign的fallback編程方式,

定義Sentinel資源后,此時如果需要針對該資源進行限流規則的配置,我們可以使用sentinel-dashboard進行配置,如圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

在pay-notify微服務節點上,選擇流控規則,并按照前面定義的Sentinel資源名稱進行限流規則配置,這里我們為了便于測驗,限流規則配置的極端所,選擇QPS方式,并定義閥值為0,此時由于已經將sentinel-dashbord與Apollo配置中心打通,因此也能從Apollo中看到已經持久化存盤的限流規則,如下圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

此時如果針對該資源方法進行網路呼叫就會被Sentinel規則限流掉,例如通過postman對/internel/pay/checkNotify介面進行網路呼叫,服務端代碼運行如下:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

可以看到此時針對該方法的呼叫已經觸發限流規則,并在拋出BlockException例外后,進入了我們前面通過@SentinelResource注解定義的blockHandler方法!

Sentinel熔斷降級編程

熔斷降級針對的是對其他服務資源進行網路呼叫時,為了防止外部服務的不穩定拖垮自身,當該服務出現不穩定狀態(例如呼叫超時或者例外比例升高等情況),對該資源的呼叫動作進行限制,從而讓請求快速失敗,避免出現級聯錯誤的情況,而當資源被降級后,在接下來的降級時間視窗內,對該資源的服務呼叫都會自動熔斷,而不會真正進行網路呼叫,而在Sentinel中則默認會拋出DegradeException例外,

從使用方向上看熔斷降級規則邏輯的發生,是發生在服務消費方,而不是服務提供方,以上述例子舉例,pay-notify服務除了針對自身介面進行限流外,pay-check對pay-notify服務的呼叫也可以進行熔斷降級處理,這里我們將pay-check服務中對pay-notify服務介面的呼叫方法進行Sentinel資源定義,代碼如下:

@SentinelResource(value = "https://www.cnblogs.com/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 呼叫其他降級服務
    return false;
}

之后我們通過Sentinel控制臺在微服務節點pay-check中針對該資源配置降級規則,如下圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

這里的意思是對該資源方法的呼叫按照平均回應時間進行熔斷降級,當1S內持續進入5個請求,對應時刻的資源平均回應時間(秒級)均超過閥值,這里配置的是200ms,那么在接下來的時間視窗內,這里配置的是10s,對該資源的呼叫都會自動進行熔斷,默認拋出DegradeException,為了演示效果,這里我們將pay-notify服務的介面回應時間故意sleep(600ms),代碼如下:

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

接下來我們通過JMeter呼叫pay-check服務,而pay-check服務將以Spring Cloud微服務的呼叫方式通過Feign來呼叫pay-notify的服務,如下圖所示:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

這里我們設定的請求方式為1s內發送7次請求呼叫,按照熔斷降級規則,由于前5個請求的回應時間都將超過200ms的閥值,因此第6、7個請求將被直接熔斷而進入fallback方法,代碼運行效果如下:

Spring Cloud微服務Sentinel+Apollo限流、熔斷實戰

 

從演示效果看熔斷降級規則已經生效,Sentinel拋出了DegradeException例外!

Sentinel與Feign的集成關系

在實際的Spring Cloud微服務開發中,微服務之間的呼叫可以通過Feign來實作,與Spring Cloud微服務官方集成的Hystrix框架一樣,在Feign中如果需要開啟Sentinel熔斷降級邏輯,需要在呼叫端(示例中為pay-check服務)配置中進行如下配置:

feign.sentinel.enabled = true

而作為Spring Cloud微服務呼叫端,在基于Feign對其他微服務存在介面呼叫的話,一般情況下我們還需要撰寫基于Feign的呼叫代碼,并指定其fallback邏輯,以本文示例為例:

@FeignClient(value = "https://www.cnblogs.com/cnchemmy/p/pay-notify", configuration = PayNotifyClientConfiguration.class, fallbackFactory = PayNotifyClientFallbackFactory.class)
public interface PayNotifyClient {
    /**
     * 支付狀態核對發生掉單現象時,通過此介面完成補單回呼操作
     */
    @RequestMapping(value = "https://www.cnblogs.com/internal/pay/checkNotify", method = RequestMethod.POST)
    boolean checkNotify(@RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/paymentId") String paymentId,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeNo") String tradeNo, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/status") int status,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platform") String platform, @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/platformtag") String platformtag,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/tradeTime") String tradeTime,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/notifyOrignMsg") String notifyOrignMsg,
            @RequestParam(value = "https://www.cnblogs.com/cnchemmy/p/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();
    }
}

需要說明的是,在微服務呼叫時,如果發送呼叫超時等情況會直接進入以上Feign所指定的fallback邏輯;而Sentinel熔斷降級規則被觸發時在某些場景下,例如在上述以平均回應時間為例的降級規則中,則只會直接進入@SentinelResource所指定的fallback方法,這一點也是Sentinel與Feign整合不夠優雅的地方,因此在撰寫容錯代碼時并不能像Hystrix那樣做到那么優雅統一!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/1834.html

標籤:C++

上一篇:C++霧中風景14:CRTP, 模板的黑魔法

下一篇:暴力列舉

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more