spring cloud feign學(xué)習(xí)三:Feign的更多使用

Ribbon配置

全局配置

由于Spring cloud Feign的客戶端負載均衡是通過spring cloud Ribbon實現(xiàn)的,所以我們可以直接通過配置Ribbon客戶端的方式來自定義各個服務(wù)客戶端調(diào)用的參數(shù)。

ribbon.ConnectTimeout=500
ribbon.ReadTimeOut=5000

指定服務(wù)配置

大多數(shù)情況下,我們對于服務(wù)調(diào)用的超時時間可能會根據(jù)實際服務(wù)的特性做一些調(diào)整,所以僅僅依靠默認的全局配置是不行的。在使用spring cloud feign的時候,針對各個服務(wù)客戶端進行個性化配置的方式與使用Spring Cloud Ribbon時的配置方式時一樣的,都采用了<client>.ribbon.key=value的格式進行設(shè)置。我們使用@Feign(value="user-service")來創(chuàng)建一個Feign客戶端的時候,同時也創(chuàng)建了一個名為user-serviceRibbon客戶端。所以我們也可以使用@Feign中的name或者value屬性只來設(shè)置對應(yīng)的ribbon參數(shù),比如:

user-service.ribbon.ConnectTimeout=500
user-service.ribbon.ReadTimeout=2000
user-service.ribbon.OkToRetryOnAllOperations=true
user-service.ribbon.MaxAutoRetriesNextServer=2
user-service.ribbon.MaxAutoRetries=1

重試機制

spring cloud Feign中默認實現(xiàn)了請求的重試機制,而上面對user-service客戶端的配置內(nèi)容就是對于請求超時以及重試配置的詳情,

@GetMapping
public String userHello() throws Exception{
   ServiceInstance serviceInstance = client.getLocalServiceInstance();
   //線程阻塞,測試超時
   int sleeptime = new Random().nextInt(3000);
   logger.info("sleeptime:"+sleeptime);
   Thread.sleep(sleeptime);
   logger.info("/user,host:"+serviceInstance.getHost()+",service id:"+serviceInstance.getServiceId()+",port:"+serviceInstance.getPort());
   return "hello world";
}
user-service.ribbon.ConnectTimeout=500
user-service.ribbon.ReadTimeout=2000
user-service.ribbon.OkToRetryOnAllOperations=true
user-service.ribbon.MaxAutoRetriesNextServer=2
user-service.ribbon.MaxAutoRetries=1

pay-service應(yīng)用中增加了重試配置參數(shù),其中,由于user-service.ribbon.MaxAutoRetries設(shè)置為1,所以重試策略先嘗試訪問首選案例一次,失敗后才更換實例訪問,而更換實例訪問的次數(shù)通過user-service.ribbon.MaxAutoRetriesNextServer參數(shù)設(shè)置為2,所以會嘗試更換兩次實例進行重試。

Ribbon的超時與Hystrix的超時是兩個概念。一般需要讓hystrix的超時時間大于Ribbon的超時時間,否則Hystrix命令超時后,改命令直接熔斷,重試機制就沒有任何意義了。

Hystrix配置

spring cloud Feign中,除了引入了用于客戶端負載均衡的spring cloud Ribbon之外,還引入了服務(wù)保護了容錯的工具Hystrixspring cloud feign客戶端的方法都封裝到Hystrix命令中進行服務(wù)保護。

全局配置

對于Hystrix的全局配置同spring cloud Ribbon的全局配置一樣,直接使用它的默認配置前綴hystrix.command.default就可以進行設(shè)置,比如設(shè)置全局的超時時間:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
在對hystrix進行設(shè)置之前,需要確認feign.hystrix.enabled參數(shù)沒有被設(shè)置為false,否則該參數(shù)設(shè)置會關(guān)閉Feign客戶端的Hystrix支持。而對于我們之前測試重試機制時,對于Hystrix的超時時間控制除了可以使用上面的配置來增加熔斷超時時間,也可以通過feign.hystrix.enabled=false來關(guān)閉Hystrix功能,或者使用hystrix.command.default.execution.timeout.enabled=false來關(guān)閉熔斷功能。

禁用Hystrix

spring cloud feign中,可以通過feign.hystrix.enabled=false來關(guān)閉Hystrix功能。另外,如果不想全局地關(guān)閉Hystrix支持,而只想針對某個服務(wù)客戶端關(guān)閉Hystrix支持時,需要通過使用@Scope("protototype")注解為指定的客戶端配置Feign.Builder實例,

@Configuration
public class DisableHystrixConfigutation {
    
    @Bean
    @Scope("prototype")
    public Feign.Builder feignBuilder(){
        return Feign.builder();
    }
}

pay-service服務(wù)的user-service接口中引入該配置。

@FeignClient(value = "user-service",configuration = DisableHystrixConfigutation.class)
...

參考資料
Feign Hystrix Support

指定命令配置

對于Hystrix命令的配置,在實際應(yīng)用時往往也會根據(jù)實際業(yè)務(wù)情況制定出不同的配置方案。配置方法也跟傳統(tǒng)的Hystrix命令的參數(shù)配置相似,采用hystrix.command.<commandKey>作為前綴。而<commandKey>默認情況下會采用feign客戶端中的方法名作為標識,所以,針對上面的hello方法,可以如下配置:

hystrix.command.hello.isolation.thread.timeoutInmilliseconds=5000

需要注意的是,由于方法名有可能會重復(fù),這個時候相同的方法名的hystrix配置會共用,所以在進行方法與配置的時候需要做好一定的規(guī)劃,當(dāng)然也可以重寫Feign.Builder的實現(xiàn),并在應(yīng)用主類中創(chuàng)建它的實例來覆蓋自動化配置的HystrixFegin.Builder實現(xiàn)。

服務(wù)降級配置

Hystrix提供的服務(wù)降級是服務(wù)容錯的重要功能,由于Spring cloud feign在定義服務(wù)客戶端的時候與Spring cloud Ribbon有很大的差別,HystrixCommand定義被封裝起來,我們無法像之前介紹spring cloud hystrix時,通過@HystrixCommand注解的fallback參數(shù)那樣來指定具體的服務(wù)降級處理方法。但是,spring cloud feign提供了另外一種簡單的定義方式,

定義一個Feign客戶端的服務(wù)降級類UserServiceFallback,實現(xiàn)UserService接口,其中每個重寫方法的實現(xiàn)邏輯都可以用來定義相應(yīng)的服務(wù)降級邏輯,具體如下:

@Component
public class UserServiceFallback implements UserService{

    @Override
    public String index() {
        return "error";
    }

    @Override
    public String hello() {
        return "hello error";
    }

    @Override
    public String hello1(String username) {
        return "hello username is null";
    }

    @Override
    public User hello2(String username, Integer age) {
        return new User("未知",0);
    }

    @Override
    public String hello3(User user) {
        return "user error";
    }
}
  • 在服務(wù)綁定接口user-service中,通過@FeignClient注解的fallback屬性來指定對應(yīng)的服務(wù)降級實現(xiàn)類:
@FeignClient(value = "user-service",fallback = UserServiceFallback.class)
public interface UserService {

    @RequestMapping("/user/index")
    String index();

    @RequestMapping("/user/hello")
    String hello();

    @RequestMapping(value = "/user/hello1",method = RequestMethod.GET)
    String hello1(@RequestParam("username") String username);

    @RequestMapping(value = "/user/hello2",method = RequestMethod.GET)
    User hello2(@RequestHeader("username") String username, @RequestHeader("age") Integer age);

    @RequestMapping(value = "/user/hello3",method = RequestMethod.POST)
    String hello3(@RequestBody User user);
}

測試,停止用戶的服務(wù):

localhost:7070/pay/hello1?username=zhihao.miao
localhost:7070/pay/hello2
localhost:7070/pay/hello3

返回了我們在UserServiceFallback中定義的每個方法的降級的重寫函數(shù)的實現(xiàn)。

fallbackFactory參數(shù)的使用

If one needs access to the cause that made the fallback trigger, one can use the fallbackFactory attribute inside @FeignClient.
如果需要訪問到造成回退的具體原因,可以使用@FeignClient.注解的fallbackFactory屬性。

@Component
public class HystrixClientFallbackFactory implements FallbackFactory<UserService> {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public UserService create(Throwable throwable) {
        logger.info("user service exception:" + throwable.getMessage());
        return new HystrixClientWithFallBackFactory(){
            @Override
            public User hello2(String username, Integer age) {
                return super.hello2(username, age);
            }
        };
    }
}

定義HystrixClientWithFallBackFactory實現(xiàn)由@Feign注解標記的接口,我這邊就實現(xiàn)了hello2方法,只測試這個方法。

public class HystrixClientWithFallBackFactory implements UserService{

    @Override
    public String index() {
        return null;
    }

    @Override
    public String hello() {
        return null;
    }

    @Override
    public String hello1(String username) {
        return null;
    }

    @Override
    public User hello2(String username, Integer age) {
        User user = new User();
        user.setAge(0);
        user.setUsername("zhihao.miao");
        user.setId(-1);
        return user;
    }

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

測試,斷開User服務(wù)之后,進入保護模式,執(zhí)行HystrixClientWithFallBackFactory中的回退操作,


控制臺上輸出錯誤日志:

參考資料
Feign Hystrix Fallbacks

其他配置

請求壓縮

spring cloud feign支持對請求與相應(yīng)進行GZIP壓縮,以減少通信過程中的性能損耗,我們只需要通過下面的兩個參數(shù)設(shè)置,就能開啟請求與相應(yīng)的壓縮功能。

feign:
  compression:
    request:
      enabled: true
    response:
      enabled: true

同時,我們還可以對請求壓縮做一些更細致的設(shè)置,比如下面的配置內(nèi)容指定壓縮的請求數(shù)據(jù)類型,并設(shè)置了請求壓縮的大小下限,只有超過了這個大小的請求才會對其進行壓縮。

feign:
  compression:
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json
      min-request-size: 2048

mime-types屬性和min-request-size都是默認值

參考資料
Feign request/response compression

日志配置

spring cloud Feign在構(gòu)建被@FeignClient注解修飾的服務(wù)客戶端時,會為每一個客戶端都創(chuàng)建一個feign.Logger實例,我們可以利用改日志對象的DEBUG模式來幫助分析Feign的請求細節(jié)。可以在application.yml配置logging.level.<feignClient>的參數(shù)配置格式來開啟指定feign客戶端的debug日志,其中<feignClient>為feign客戶端定義接口的完整路徑

logging:
  level: 
    com.zhihao.miao.pay.service.UserService: debug

但是,只添加了該配置還無法實現(xiàn)對debug日志的輸出。這是因為feign客戶端默認的Logger.Level對象定義為NONE級別,該級別不會記錄任何Feign調(diào)用過程中的信息,所以我們需要調(diào)整它的級別,針對全局的日志級別,可以在應(yīng)用主類中加入Logger.Level的Bean創(chuàng)建,

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class PayApplication {

    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class,args);
    }

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}

也可以實現(xiàn)配置類,然后在Feign客戶端來指定配置類以實現(xiàn)是否需要調(diào)整不同的日志級別,比如說下面的實現(xiàn):

@Configuration
public class FullLogConfiguration {

    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }
}
@FeignClient(value = "user-service",configuration = FullLogConfiguration.class)
public interface UserService {

    @RequestMapping("/user/index")
    String index();

    @RequestMapping("/user/hello")
    String hello();

    @RequestMapping(value = "/user/hello1",method = RequestMethod.GET)
    String hello1(@RequestParam("username") String username);

    @RequestMapping(value = "/user/hello2",method = RequestMethod.GET)
    User hello2(@RequestHeader("username") String username, @RequestHeader("age") Integer age);

    @RequestMapping(value = "/user/hello3",method = RequestMethod.POST)
    String hello3(@RequestBody User user);
}

訪問localhost:7070/pay/hello1?username=zhihao.miao接口,控制臺上打印如下信息:

對于Feign的Logger級別主要有下面4類,可根據(jù)實際需要進行調(diào)整使用:

  • none:不記錄任何信息
  • basic:僅記錄請求方法,url以及響應(yīng)狀態(tài)碼和執(zhí)行時間
  • headers:除了記錄basic級別的信息之外,還會記錄請求和響應(yīng)的頭信息。
  • FULL:記錄所有請求與響應(yīng)的明細,包括頭信息,請求體,元數(shù)據(jù)等。

參考資料
Feign logging

代碼地址
代碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容