天天看點

Sentinel全局Feign預設熔斷設計實作

Sentinel的降級熔斷的配置,生産環境使用的時候,一般會在控制台管理,持久化到Nacos;微服務監聽Nacos的配置變化,進而實作服務調用的降級熔斷政策。

寫在前面

用XMind畫了一張導圖記錄Spring Cloud Alibaba的學習筆記(源檔案對部分節點有詳細備注和參考資料,由于太大就沒展示全部,歡迎關注我的公衆号:阿風的架構筆記 背景發送【導圖】拿下載下傳連結, 已經完善更新):

Sentinel全局Feign預設熔斷設計實作

前言

思考這個問題:

Sentinel全局Feign預設熔斷設計實作

Sentinel的降級熔斷的配置,生産環境使用的時候,一般會在控制台管理,持久化到Nacos;微服務監聽Nacos的配置變化,進而實作服務調用的降級熔斷政策。

現在就會遇到這樣的問題,如果有很多Feign接口,如上圖服務A、服務B都有一些Feign接口的遠端調用,都需要我們進行一一配置。而且配置的一些參數絕大多數都一樣的。如:

1、對Feign遠端調用的慢響應政策的配置降級政策

2、對Feign遠端調用的異常數的配置降級政策

3、對Feign遠端調用的異常比例數的配置降級政策

針對上面的配置1-2個服務方法還好;但是現在公司的生産環境都有100~200個微服務,服務之間的調用方法就更多了;那針對普通标準的降級熔斷的配置都需要人工一個個配置,那是不是太麻煩了。

本文就來解決這個問題,跟着繼續往下看。

源碼分析

我們先來看看Sentinel是怎麼設定熔斷政策的,在上圖中我們知道是通過Sentinel控制台進行配置,然後微服務都可以訂閱這些配置;我們看一下源碼。

Sentinel全局Feign預設熔斷設計實作

這個是監聽nacos配置的相關的代碼

Sentinel全局Feign預設熔斷設計實作

上圖代碼核心就是發現配置有變化,就updateValue規則;我們繼續跟蹤代碼發現一個DegradeRuleManager降級規則的管理類,裡面有2個核心的變量ruleMap、circuitBreakers;我們可以猜出就是降級規則集合以及熔斷規則集合。

Sentinel全局Feign預設熔斷設計實作

在繼續往下看,我們發現有個RulePropertyListener中reloadFrom方法****,即重新加載規則;方法裡面有個buildCircuitBreakers方法,一看方法名就知道是建構熔斷政策。

Sentinel全局Feign預設熔斷設計實作

在看一下buildCircuitBreakers方法,我們看到本質就是周遊DegradeRule集合,然後在初始化熔斷對象CircuitBreaker。

Sentinel全局Feign預設熔斷設計實作

這裡我們知道熔斷是怎麼産生的了;本質就是通過DegradeRule産生的。

解決方案

上面我們知道了一些熔斷對象産生的原理,我們隻要可以自定義DegradeRule對象就可以産生。我們在學習Sentinel的時候,他有個Api方式去定義降級規則,大家可以去看一下之前的文章,詳細介紹了Api定義規則的方式。我們看一下案例

Sentinel全局Feign預設熔斷設計實作

我們可以看到DegradeRule對象的定義,以及DegradeRuleManager對象;上面的代碼就能給資源名api定義了慢響應的降級政策了。

講到這裡聰明的小夥伴們有沒有想到一些思路呢?往下看。

方案思路

先給出整體的解決思路

Sentinel全局Feign預設熔斷設計實作

上圖中介紹的流程

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的源碼實作的方法,看代碼。

Sentinel全局Feign預設熔斷設計實作

上面是根據啟動服務時,堆棧資訊擷取main方法的啟動類對象。

Sentinel全局Feign預設熔斷設計實作

根據啟動類對象,擷取到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);//擷取變量的值
           
Sentinel全局Feign預設熔斷設計實作

核心代碼

上面的技術難點解決掉之後,我們就放開雙手撸代碼了,這裡貼上核心的代碼;小夥伴們。

public class DegradeRuleInitializer implements ApplicationRunner, EnvironmentAware
           

實作ApplicationRunner, EnvironmentAware就能夠實作啟動時,去掃描了,入口就在ApplicationRunner中的run方法。

Sentinel全局Feign預設熔斷設計實作

掃描類

Sentinel全局Feign預設熔斷設計實作

掃描FeignClient

Sentinel全局Feign預設熔斷設計實作

初始化預設規則

Sentinel全局Feign預設熔斷設計實作

設定了預設降級規則,把配置資訊釋出到nacos

Sentinel全局Feign預設熔斷設計實作

效果

一旦微服務啟動了,nacos配置就有了

Sentinel全局Feign預設熔斷設計實作

我們會把預設的值釋出到nacos裡面,小夥伴們可以具體看一些資源名,裡面就會有很多降級規則。

Sentinel全局Feign預設熔斷設計實作

我們再來看看Sentinel控制台,裡面就顯示了降級規則清單;設計的是針對同一個資源名做異常數、異常比例、慢響應三種類型的降級熔斷政策。

Sentinel全局Feign預設熔斷設計實作

全局預設的值,到底是多少閥值,是可以通過配置的方式的,這些就不介紹了。比較簡單。

到這裡就全部實作了微服務中Feign接口的降級熔斷政策的預設化配置,不需要人工去添加了;當然是支援人工去修改的,如果需要修改閥值,可以人工修改。

總結

本文介紹了Sentinel的全局Feign預設熔斷的技術實作方案,整體思路原理不是太複雜,就是利用其本身的功能,做了一些擴充;這樣更友善使用者的使用。

看完三件事❤️

如果你覺得這篇内容對你還蠻有幫助,我想邀請你幫我三個小忙:

  1. 點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力。
  2. 關注公衆号 『 阿風的架構筆記 』,不定期分享原創知識。
  3. 同時可以期待後續文章ing