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-service
的Ribbon
客戶端。所以我們也可以使用@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ù)保護了容錯的工具Hystrix
,spring 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)
...
指定命令配置
對于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中的回退操作,
控制臺上輸出錯誤日志:
其他配置
請求壓縮
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
代碼地址
代碼地址