首頁 > 軟體

hystrix設定中Apollo與Archaius對比分析

2022-02-21 16:01:18

前言

feign是一個出色的Http請求使用者端封裝框架,feign-hystrix是整個框架體系裡的其中一個模組,用來整合hystrix熔斷器的,feign和hystrix這兩個專案都是Netflix開源的(openfeign已獨立迭代)。在spring boot專案中,可以使用spring-cloud-starter-openfeign模組,無縫整合feign和hystrix。但是,hystrix預設採用的Archaius來驅動hystrix的設定系統,無縫整合的同時,也會把archaius-core給引入進來。archaius是一個設定中心專案,類似spring cloud config和apollo,如果archaius只是作為hystrix設定的驅動,專案啟動時會列印煩人的警告紀錄檔,提示你沒有設定任何動態設定源。當專案裡已經採用了apollo時,可以直接剔除掉Archaius,他們的功能定位高度重合了。直接剔除依賴,會導致原本設定在spring中的設定不生效,博主也是在不小心剔除後,遇到了設定不生效的問題,才有了本篇博文,記錄下過程。只要稍加改動,結合apollo設定動態下發能力,可以做到hystrix的設定實時動態生效。

ARCHAIUS警告紀錄檔

2020-12-10 11:19:41.766 WARN 12835 --- [ main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.
2020-12-10 11:19:41.766 INFO 12835 --- [ main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.
2020-12-10 11:19:41.772 WARN 12835 --- [ main] c.n.c.sources.URLConfigurationSource : No URLs will be polled as dynamic configuration sources.
2020-12-10 11:19:41.772 INFO 12835 --- [ main] c.n.c.sources.URLConfigurationSource : To enable URLs as dynamic configuration sources, define System property archaius.configurationSource.additionalUrls or make config.properties available on classpath.

我們遇到的問題

在一次系統優化重構中,博主給整個專案來了一個360的大瘦身,把所有的未使用的依賴統統給挪走了。其中就包括了spring-cloud-starter-openfeign模組的archaius-core依賴。因為我們已經使用了apollo設定中心,archaius在這個專案裡顯得很多餘,而且還會列印煩人的警告紀錄檔。所以就直接排除了,如:

implementation ('org.springframework.cloud:spring-cloud-starter-openfeign'){
    exclude(module:"archaius-core")
}

為此,專門瞭解了下archaius的來歷,並且針對feign的熔斷器的Fallback能力進行了測試,一切執行正常。上線一週後,問題暴露出來了,同事反饋,hystrix的設定好像不生效了。現象是,原本設定的hystrix執行緒執行不超時,卻發生了很多執行一秒就超時了,我們的關鍵設定如下(這不是一個很好的設定示範,後面會調整更細粒度控制):

#禁止執行超時
hystrix.command.default.execution.timeout.enabled = false

直觀感覺就是這個設定不生效了,聯想到archaius-core被移除,所以先立馬恢復了依賴,重新打包上線,問題解決。就這?為了徹底搞清楚Hystrix的設定載入過程,我們對feign整合hystrix進行了全面的瞭解。

HYSTRIX在FEIGN中的載入過程

在spring-cloud-starter-openfeign的封裝下,使用起來非常簡單,但是內部的載入流程非常複雜。所以博主也不打算全面鋪開來說這塊內容,有機會會獨立一篇來說。這裡根據我們上文遇到的禁用執行超時不生效的問題,博主總結了載入流程中的幾個關鍵的地方:

Feign和Hystrix的橋接器Feign-Hystrix

這個專案是feign和hystrix的橋接器,通過這樣的一個橋接器,將兩個框架的api能力整合在了一起,下面簡要闡述下,載入過程關鍵類的作用:

SetterFactory:承載了構造HystrixCommand範例的所有的設定的介面,有一個預設實現Default,在下面會用到,是自定義設定實現的突破口

HystrixInvocationHandler:這是一個實現了JDK代理介面類,用來代理Feign最終的執行,HystrixCommand類就是在這個範例裡被構造執行的,使用的構造方法正是帶入參Setter的構造方法,整合方會實現SetterFactory來構造Setter。偵錯程式時我們將端點打進這個類裡,就可以看到設定載入的情況

SPRING BOOT自動載入HYSTRIX

@Configuration
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
protected static class HystrixFeignConfiguration {
   @Bean
   @Scope("prototype")
   @ConditionalOnMissingBean
   @ConditionalOnProperty(name = "feign.hystrix.enabled")
   public Feign.Builder feignHystrixBuilder() {
      return HystrixFeign.builder();
   }
}

這裡是Hystrix在feign框架下載入的總入口。這個預設的構建器Builder中,有一個預設實現的SetterFactory,這個SetterFactory專門負責傳遞引數給Hystrix初始化HystrixCommand用。可以看到這裡Bean的範例化加上了@ConditionalOnMissingBean條件約束,既我們可以自定義實現Hystrix的構造器,覆蓋這裡的實現,在自定義的構造器中,可以通過自定義實現SetterFactory,來注入任意的設定。這個是實現Hystrix設定自定義載入的方式之一,不過不推薦,沒必要破壞spirng現有的這種結構,而且程式碼也會比較冗長(下面{...}省略了一百多行設定處理程式碼,用來相容Hystrix現有設定定義),看起來如下: 

HYSTRIX的動態兜底設定

設定是hystrix的核心,各種策略的選擇執行都需要設定來驅動,所以,雖然在應用層面不需要太多的設定設定,但是必要的設定hystrix都會填充一個預設值,比如,hystrix預設執行超時設定的1s。Hystrix中的設定有三個層次的載入優先順序,如:

  • 最先載入Setter:Setter是使用者傳遞給Hystrix構造器的,所以優先順序別最高
  • 其次載入動態設定源:如果必要的設定在Setter裡沒有找到,則在動態設定源中獲取
  • 最後載入預設設定:如果動態設定源中也沒有找到設定,則採用預設的設定

其中動態設定源,有一個基於SystemProperties的設定實現HystrixDynamicPropertiesSystemProperties。HystrixCommand在範例化時,如果使用者沒有給到具體的設定,Hystrix每次都會去SystemProperties中尋找設定。也就是說,我們可以通過-D引數注入任意Hystrix的設定引數,都會生效。有了這個特性,可以非常簡單的結合apollo,達到hystrix設定動態生效的效果,而且所有設定相容Hystrix原本的設定。

APOLLO設定驅動HYSTRIX

實現這個功能的關鍵是。系統初始化時,將hystrix.command字首相關的設定從apollo中獲取到然後統統注入SystemProperties。設定更新時,同時更新SystemProperties中的設定即可,非常簡單,用程式碼說話:

/**
 * @author kl (http://kailing.pub)
 * @since 2020/12/10
 */
@Slf4j
@Configuration
@AutoConfigureBefore(value = {FeignClientsConfiguration.class, FeignAutoConfiguration.class})
public class HystrixConfiguration{
    public static final String DYNAMIC_TAG = "dynamic.";
    public static final String DYNAMIC_PREFIX = DYNAMIC_TAG + "hystrix.command.";
    public static final String PREFIX = "hystrix.command.";
    @ApolloConfig
    private Config config;
    @PostConstruct
    public void initHystrix(){
        this.config.addChangeListener(
                event -> this.loadHystrixConfig(event.changedKeys()),
                null,
                Sets.newHashSet(DYNAMIC_PREFIX)
        );
        this.loadHystrixConfig(config.getPropertyNames());
    }
    private void loadHystrixConfig(Setconfigkyes) {
        configkyes.forEach(key -> {
            if (StringUtils.containsIgnoreCase(key, PREFIX)) {
                String value = config.getProperty(key, null);
                String realKey = key.replaceAll(DYNAMIC_TAG,"").trim();
                System.setProperty(realKey, value);
                log.info("Hystrix config: {}={}", key, value);
            }
        });
    }
}

這裡注意一個問題:為啥這裡多設計了一個dynamic.字首的設定,這是因為博主在測試過程中觸發了apollo設定監聽器隱藏的問題,導致Apollo的動態監聽器不生效了。Apollo設定載入是以SystemProperties為最高優先順序的,當設定發生變化時,apollo會將SystemProperties覆蓋到設定之後,才比較本次設定釋出是否有更新。因為我們一開始就將相關的設定載入到SystemProperties裡了,所以每次變更都會被覆蓋成之前的值,導致更新判斷失效,一直進不了監聽器。如果想要動態更新,就需要維護一份apollo的設定和SystemProperties裡的對映關係,而不能保持一致,這樣每次修改apollo時,就可以將維護對映關係的字首去掉,然後將值動態更新到SystemProperties。目前的設計裡,既支援原生的所有設定一次性載入,也支援dynamic.字首拼裝原有設定動態載入
設定範例

#初始化時一次性載入
hystrix.command.default.execution.timeout.enabled = true
#每次修改動態生效
dynamic.hystrix.command.default.execution.timeout.enabled = true

結語

Feign-hystrix的設定,有了Apollo,還用Archaius嗎?當然不用,採用apollo實現方案,既相容了所有原生設定,還可以做到動態生效,豈不美哉。

以上就是hystrix設定中Apollo與Archaius對比分析的詳細內容,更多關於hystrix設定Apollo與Archaius對比的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com