前言
上篇文章中介紹了Sentinel全局默認熔斷,留下了一個思考:
Sentinel的降級熔斷的配置,生產環境使用的時候,一般會在控制台管理,持久化到Nacos;微服務監聽Nacos的配置變化,從而實現服務調用的降級熔斷策略。
現在就會遇到這樣的問題,如果有很多Feign接口,如上圖服務A、服務B都有一些Feign接口的遠程調用,都需要我們進行一一配置。而且配置的一些參數絕大多數都一樣的。如:
1、對Feign遠程調用的慢響應策略的配置降級策略
2、對Feign遠程調用的異常數的配置降級策略
3、對Feign遠程調用的異常比例數的配置降級策略
針對上面的配置1-2個服務方法還好;但是現在公司的生產環境都有100~200個微服務,服務之間的調用方法就更多了;那針對普通標准的降級熔斷的配置都需要人工一個個配置,那是不是太麻煩了。
雖然之前的文章中,我們已經解決了Feign的全局默認降級fallback配置,但是他沒法實現熔斷(即沒有熔斷功能,每次都要去調用異常服務方法,然后再降級)。
本文就來解決這個問題,跟着繼續往下看。
源碼分析
我們先來看看Sentinel是怎么設置熔斷策略的,在上圖中我們知道是通過Sentinel控制台進行配置,然后微服務都可以訂閱這些配置;我們看一下源碼。
這個是監聽nacos配置的相關的代碼
上圖代碼核心就是發現配置有變化,就updateValue規則;我們繼續跟蹤代碼發現一個DegradeRuleManager降級規則的管理類,里面有2個核心的變量ruleMap、circuitBreakers;我們可以猜出就是降級規則集合以及熔斷規則集合。
在繼續往下看,我們發現有個RulePropertyListener中reloadFrom方法****,即重新加載規則;方法里面有個buildCircuitBreakers方法,一看方法名就知道是構建熔斷策略。
在看一下buildCircuitBreakers方法,我們看到本質就是遍歷DegradeRule集合,然后在初始化熔斷對象CircuitBreaker。
這里我們知道熔斷是怎么產生的了;本質就是通過DegradeRule產生的。
解決方案
上面我們知道了一些熔斷對象產生的原理,我們只要可以自定義DegradeRule對象就可以產生。我們在學習Sentinel的時候,他有個Api方式去定義降級規則,大家可以去看一下之前的文章,詳細介紹了Api定義規則的方式。我們看一下案例
我們可以看到DegradeRule對象的定義,以及DegradeRuleManager對象;上面的代碼就能給資源名api定義了慢響應的降級策略了。
講到這里聰明的小伙伴們有沒有想到一些思路呢?往下看。
方案思路
先給出整體的解決思路
上圖中介紹的流程
1、啟動服務時掃描jar,獲取@FeignClient注解的接口(技術難點一:掃描哪些jar包)
2、獲得Feign接口中的調用方法
3、服務本地創建DegradeRule對象。(技術難點二:Sentinel的資源名支持動態配置)
4、把設置的默認的降級熔斷規則同步到Nacos
根據上面的流程,我們就可以看到,一旦微服務啟動了,就會自動把Feign接口配置默認的降級熔斷規則,以及同步到Nacos中;再結合之前文章中介紹的Sentinel控制台改造,就立刻在控制台顯示這些降級規則了,而不需要認為配置了。
注意:上面只是介紹了整體流程,在編寫代碼的時候,我們需要考慮到很多場景,如:
一)服務第二次啟動的時候,nacos中已經有了相關的配置,是否還要修改nacos的配置。
二)以及有些特殊業務在Sentinel控制台進行了降級配置,那默認的全局配置如何兼容人工的配置。
這些就不在這里講了,本文只介紹核心方案思路,核心代碼
其他的有興趣的小伙伴們,可以跟要源碼
技術難點
難點一
我們掃碼jar包,而且是要掃碼包含@FeignClient注解接口的jar。我們知道在使用Feign功能的時候,需要在SpringBootApplication啟動類中加上@EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class ApplicationA {
public static void main(String[] args) {
SpringApplication.run(ApplicationA.class, args);
}
}
有的時候Feign包會用第三方jar的形式存在,那代碼就有會變成
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"com.rainbow.demo1.feign","com.rainbow.demo2.feign"})
public class ApplicationA {
public static void main(String[] args) {
SpringApplication.run(ApplicationA.class, args);
}
}
里面的@EnableFeignClients注解的屬性basePackages中顯式的指向了Feign包的位置了,這個比較好弄,直接用用ClassScan工具類掃就行了。
ClassScan工具類是支持子包掃描的
那沒有顯式的定義basePackages,那怎么獲取到jar包路徑呢?
我們可以參考SpringCloud的源碼實現的方法,看代碼。
上面是根據啟動服務時,堆棧信息獲取main方法的啟動類對象。
根據啟動類對象,獲取到EnableFeignClients對象,如果沒有basePackages,那就是以啟動類的包為掃描的入口。
這樣我們就解決了掃描jar入口的問題。
難點二
常規方式
資源名的獲取,舉個例子
@FeignClient(name = "service-provider")
public interface ProviderServiceFeign {
@GetMapping("/transferHeaders")
public BaseRestResponse<String> transferHeaders();
}
根據微服務的Sentinel資源名定義,@FeignClient(name = "service-provider"),微服務名是service-provider;那針對transferHeaders()方法的降級策略資源名即為
lb://service-provider/transferHeaders
這個實現比較簡單就是獲取@FeignClient的name的值,以及方法@GetMapping里面的值就可以拼接出資源名。
指定Url地址
@FeignClient(name = "service-provider",url = "http://xxxxx")
public interface ProviderServiceFeign {
@GetMapping("/transferHeaders")
public BaseRestResponse<String> transferHeaders();
}
指定url的目的其實就是指定請求的方式,這種情況的Sentinel的資源名即為
http://xxxxx/transferHeaders
這個技術實現也比較簡單,只需要考慮到這個場景,就可以了。
動態配置Url
還有一種情況即對接第三方平台時,我們一般不會寫死Url,而是通過配置的方式,如
@FeignClient(name = "service-provider",url = "${reqUrl}")
public interface ProviderServiceFeign {
@GetMapping("/transferHeaders")
public BaseRestResponse<String> transferHeaders();
}
上面的${reqUrl}是通過配置的,那Sentinel的資源名是什么樣的呢?本質上面資源名也是Url+具體的請求地址,即
http://${reqUrl}/transferHeaders
但是這樣設置資源名肯定是不正確的,需要把具體的配置值拿過來拼接。那我們就需要在程序中獲取${reqUrl}的值,講到這里小伙伴們知道怎么實現了嗎?其實就是用到
Environment environment ;//環境變量對象
this.environment.resolvePlaceholders(url);//獲取變量的值
核心代碼
上面的技術難點解決掉之后,我們就放開雙手擼代碼了,這里貼上核心的代碼;小伙伴們。
public class DegradeRuleInitializer implements ApplicationRunner, EnvironmentAware
實現ApplicationRunner, EnvironmentAware就能夠實現啟動時,去掃描了,入口就在ApplicationRunner中的run方法。
掃描類
掃描FeignClient
初始化默認規則
設置了默認降級規則,把配置信息發布到nacos
效果
一旦微服務啟動了,nacos配置就有了
我們會把默認的值發布到nacos里面,小伙伴們可以具體看一些資源名,里面就會有很多降級規則。
我們再來看看Sentinel控制台,里面就顯示了降級規則列表;設計的是針對同一個資源名做異常數、異常比例、慢響應三種類型的降級熔斷策略。
全局默認的值,到底是多少閥值,是可以通過配置的方式的,這些就不介紹了。比較簡單。
到這里就全部實現了微服務中Feign接口的降級熔斷策略的默認化配置,不需要人工去添加了;當然是支持人工去修改的,如果需要修改閥值,可以人工修改。
總結
本文介紹了Sentinel的全局Feign默認熔斷的技術實現方案,整體思路原理不是太復雜,就是利用其本身的功能,做了一些擴展;這樣更方便用戶的使用。小伙伴如果需要源代碼可以聯系哦。謝謝!!!
java高級開發系統進階筆記、最新面試復習筆記PDF,我的GitHub