【Spring Cloud總結】21.單個FeginClient禁用Hystrix

接上篇《20.Feign對Hystrix的支持》  Spring Cloud版本爲Finchley.SR2版

上一篇我們簡單介紹瞭如何開啓Feign對Hystrix的支持,並演示了實現FeignClient接口以及FallbackFactory接口兩種形式的降級服務。下面我們來學習一下,在存在多個FeignClient的情況下,如何禁用單個的Hystrix。
本部分官方文檔:https://cloud.spring.io/spring-cloud-static/Finchley.SR2/single/spring-cloud.html

一、單個Feign Client禁用Hystrix原理

通過上一篇的學習我們可以知道,如果對Feign需要開啓或者禁用Hystrix,需要在配置文件中設置feign.hystrix.enabled參數。但是要知道,feign.hystrix.enabled參數,是設置全局的Feign是否支持Hystrix,但是假如我們的應用中有多個Feign Cleint對象,想讓其中一部分支持Hystrix,一部分不支持,該怎麼辦呢?這裏就需要提到之前Feign的默認配置了。

回憶一下,Feign組件支持我們對其的核心參數進行自定義配置化,它的默認配置是FeignClientsConfiguretion,默認配置項有以下幾個參數:
Decoder feignDecoder: ResponseEntityDecoder (爲SpringDecoder的包裝類)
Encoder feignEncoder: SpringEncoder
Logger feignLogger: Slf4jLogger
Contract feignContract: SpringMvcContract
Feign.Builder feignBuilder: HystrixFeign.Builder
Client feignClient: 如果啓用的Ribbon,則爲LoadBalancerFeignClient, 否則使用默認的FeignClient。
這裏Decoder爲請求信息的編碼器,Encoder爲請求信息的解碼器,Logger爲日誌類, Contract爲Feign的契約類(功能是將我們傳入的接口進行解析驗證,看註解的使用是否符合規範,然後返回給我們接口上各種相應的元數據),Builder爲Feign的構造類(默認爲HystrixFeign的Builder),然後就是Feign的Client。

我們要實現爲單個FeignClient禁用Hystrix,就需要在自定義配置文件中修改其Feign.Builder參數,即修改Feign的構造類。官方文檔提供的方法爲:

那爲什麼要修改其構造類,添加上“@Scope("prototype")”註解,就能夠實現禁用單個Feign的效果呢?
默認情況下,Feign是整合了Hystrix的,我們打開Spring Cloud中Feign組件的FeignClientsConfiguretion類查看其有關Budiler的配置項(在spring-cloud-openfeign-core-2.0.2.RELEASE.jar包下):

Budiler部分配置代碼:

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

我們可以看到,在Feign的默認配置類中,有關於Feign構造器默認使用的就是HystrixFeign的builder,即支持Hystrix的構造器。如果我們不需要該FeignClient支持Hystrix,在自定義的配置類中,將Budiler定義爲Feign自己的不支持Hystrix的構造類即可。

最後順便說一下“@Scope("prototype")”註解,這個其實我們在之前在《Hystrix的CommandProperties配置》一章的“Hystrix線程隔離策略”小節中已經提到過,“Scope”就是Spring定義bean的生命週期的參數,常用的有singleton和prototype兩種,其中:
(1)singleton:爲“單一實例”的意思,一個容器中只存在一個實例,所有對該類型bean的依賴都引用這一單一實例。
(2)prototype:容器在接受到該類型的對象的請求的時候,會每次都重新生成一個新的對象給請求方。
很顯然,Feign對於Builder的Bean的管理,是希望其接受到該類型對象的請求時,每次都生成新對象,

二、實例測試

理解了如何禁用翻個Feign組件的Hystrix後,我們在之前的工程上試驗一下。之前Movie工程中只有一個UserFeignClient和自定義配置類TestFeignConfiguration:

我們再添加一個UserFeignClient2以及自定義配置類TestFeignConfiguration2:

UserFeignClient2代碼:

package com.microserver.cloud;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;

import com.microserver.cloud.entity.User;
import com.microserver.config.TestFeignConfiguration2;

import feign.Param;
import feign.RequestLine;
import feign.hystrix.FallbackFactory;

/**
 * 當@FeignClient有name和url還有configuration時,取值爲url的地址,name只是爲一個名稱而已(無意義)
 * 當@FeignClient只有name和configuration時,name的取值爲eureka中的application項目的名稱即虛擬地址
 *
 */
@FeignClient(name="microserver-provider-user2",url="http://localhost:7902",configuration = TestFeignConfiguration2.class,fallbackFactory=FeignClientFallbackFactory2.class)
public interface UserFeignClient2 {
    
    @RequestLine("GET /findById/{id}")
    public User findById(@Param("id") Long id);
    
    @RequestLine("POST /postUser")
    public String postUser(@RequestBody User user);
    
}

@Component
class FeignClientFallbackFactory2 implements FallbackFactory<UserFeignClient2>{

    //日誌對象
    private static final Logger LOGGER = LoggerFactory.getLogger(FeignClientFallbackFactory2.class);

    @Override
    public UserFeignClient2 create(Throwable cause) {
        return new UserFeignClient2() {
            
            @Override
            public User findById(Long id) {
                // 日誌最好放在各個fallback方法中,而不要直接放在create方法中。
                // 否則在引用啓動時,就會打印該日誌
                FeignClientFallbackFactory2.LOGGER.info("fallback2; reason was: ", cause);
                
                User user = new User();
                user.setId(0L);
                user.setName("FeignClient2 降級服務");
                return user;
            }

            @Override
            public String postUser(User user) {
                return null;
            }
        };
    }
}

需要注意的是,這裏的@FeignClient註解的name一定不要和之前的一樣,因爲如果有多個@FeignClient註解使用了相同的name屬性,則註解的configuration參數會被覆蓋。至於誰覆蓋誰要看Spring容器初始化Bean的順序。所以爲了避免混亂,這裏我們隨意定義了一個name,當然這個name對應的application實例是不存在的,直接會去取url的服務地址作爲請求地址。

這裏我們依然爲其添加了fallbackFactory參數,是爲了測試引用是否有效。如果禁用有效,就不會進入降級方法。配置類TestFeignConfiguration2代碼:

package com.microserver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import feign.Feign;

@Configuration
public class TestFeignConfiguration2 {
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder() {
        return Feign.builder();
    }
    
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
    
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}

這裏設置Builder爲Feign的原始Builder。然後在Controller中添加一個新的請求響應方法,來試驗UserFeignClient2:

@Autowired
private UserFeignClient2 userFeignClient2;

@GetMapping("/movie2/{id}")
public User findUserById2(@PathVariable Long id){
    return userFeignClient2.findById(id);
}

然後我們啓動之前的服務提供者user工程、服務消費者movie工程以及eureka Server註冊中心,訪問“movie/1”服務以及“movie2/1”是好的:


然後關掉服務提供者user工程,再次訪問“movie/1”服務,可以看到此時進入了降級服務中:

然後我們再訪問“movie2/1”服務,發現沒有進入降級服務,而是進行了報錯:


說明我們爲單個FeignClient禁用Hystrix是成功的。

三、爲單個FeignClient禁用Hystrix的意義

雖然我們上面實現了爲單個FeignClient禁用Hystrix的效果,但是看起來好像沒有什麼實現的意義,貌似看起來有Hystrix的支持更加穩妥一些。那麼究竟什麼樣的場景下,需要對單個FeignClient的Hystrix進行禁用呢?

spring cloud通過feign client進行服務之間調用的時候,默認不會進行重試,這樣會有一個問題,比如你的服務在滾動升級重啓的時候,feign的調用將直接失敗,但其實滾動重啓情況下,重啓了一個服務實例,此時還有另外一個服務實例是可用的,應該允許自動均衡策略重試請求發送到另外一個可用的服務實例上去。

上面就是feign的重複請求策略,hystrix的熔斷機制可能會導致fegin的重複請求策略無效(重複請求的超時時間必須小於hystrix的熔斷時間)。所以在某些情況下就需要選擇關閉fegin的hystrix。

參考:《51CTO學院Spring Cloud高級視頻》
https://blog.csdn.net/xiao_jun_0820/article/details/79292461

轉載請註明出處:https://blog.csdn.net/acmman/article/details/102069407

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章