微服務實戰SpringCloud之Feign簡介及使用


Feign的目標

feign是聲明式的web service客戶端,它讓微服務之間的調用變得更簡單了,類似controller調用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign時提供負載均衡的http客戶端。

引入Feign

項目中使用了gradle作為依賴管理,maven類似。

dependencies {
    //feign
    implementation('org.springframework.cloud:spring-cloud-starter-openfeign:2.0.2.RELEASE')
    //web
    implementation('org.springframework.boot:spring-boot-starter-web')
    //eureka client
    implementation('org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:2.1.0.M1')
    //test
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

因為feign底層是使用了ribbon作為負載均衡的客戶端,而ribbon的負載均衡也是依賴於eureka 獲得各個服務的地址,所以要引入eureka-client。

SpringbootApplication啟動類加上@FeignClient注解,以及@EnableDiscoveryClient。

@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } } 

yaml配置:

server: port: 8082 #配置eureka eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: status-page-url-path: /info health-check-url-path: /health #服務名稱 spring: application: name: product profiles: active: ${boot.profile:dev} #feign的配置,連接超時及讀取超時配置 feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic 

Feign的使用

@FeignClient(value = "CART") public interface CartFeignClient { @PostMapping("/cart/{productId}") Long addCart(@PathVariable("productId")Long productId); } 

上面是最簡單的feign client的使用,聲明完為feign client后,其他spring管理的類,如service就可以直接注入使用了,例如:

//這里直接注入feign client @Autowired private CartFeignClient cartFeignClient; @PostMapping("/toCart/{productId}") public ResponseEntity addCart(@PathVariable("productId") Long productId){ Long result = cartFeignClient.addCart(productId); return ResponseEntity.ok(result); } 

可以看到,使用feign之后,我們調用eureka 注冊的其他服務,在代碼中就像各個service之間相互調用那么簡單。

FeignClient注解的一些屬性

屬性名 默認值 作用 備注
value 空字符串 調用服務名稱,和name屬性相同  
serviceId 空字符串 服務id,作用和name屬性相同 已過期
name 空字符串 調用服務名稱,和value屬性相同  
url 空字符串 全路徑地址或hostname,http或https可選  
decode404 false 配置響應狀態碼為404時是否應該拋出FeignExceptions  
configuration {} 自定義當前feign client的一些配置 參考FeignClientsConfiguration
fallback void.class 熔斷機制,調用失敗時,走的一些回退方法,可以用來拋出異常或給出默認返回數據。 底層依賴hystrix,啟動類要加上@EnableHystrix
path 空字符串 自動給所有方法的requestMapping前加上前綴,類似與controller類上的requestMapping  
primary true    

此外,還有qualifier及fallbackFactory,這里就不再贅述。

Feign自定義處理返回的異常

這里貼上GitHub上openFeign的wiki給出的自定義errorDecoder例子。

public class StashErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { if (response.status() >= 400 && response.status() <= 499) { //這里是給出的自定義異常 return new StashClientException( response.status(), response.reason() ); } if (response.status() >= 500 && response.status() <= 599) { //這里是給出的自定義異常 return new StashServerException( response.status(), response.reason() ); } //這里是其他狀態碼處理方法 return errorStatus(methodKey, response); } } 

自定義好異常處理類后,要在@Configuration修飾的配置類中聲明此類。

Feign使用OKhttp發送request

Feign底層默認是使用jdk中的HttpURLConnection發送HTTP請求,feign也提供了OKhttp來發送請求,具體配置如下:

feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic okhttp: enabled: true hystrix: enabled: true 

Feign原理簡述

  • 啟動時,程序會進行包掃描,掃描所有包下所有@FeignClient注解的類,並將這些類注入到spring的IOC容器中。當定義的Feign中的接口被調用時,通過JDK的動態代理來生成RequestTemplate。
  • RequestTemplate中包含請求的所有信息,如請求參數,請求URL等。
  • RequestTemplate聲場Request,然后將Request交給client處理,這個client默認是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等。
  • 最后client封裝成LoadBaLanceClient,結合ribbon負載均衡地發起調用。

詳細原理請參考源碼解析。

Feign、hystrix與retry的關系請參考https://xli1224.github.io/2017/09/22/configure-feign/

Feign開啟GZIP壓縮

Spring Cloud Feign支持對請求和響應進行GZIP壓縮,以提高通信效率。

application.yml配置信息如下:

feign: compression: request: #請求 enabled: true #開啟 mime-types: text/xml,application/xml,application/json #開啟支持壓縮的MIME TYPE min-request-size: 2048 #配置壓縮數據大小的下限 response: #響應 enabled: true #開啟響應GZIP壓縮 

注意:

由於開啟GZIP壓縮之后,Feign之間的調用數據通過二進制協議進行傳輸,返回值需要修改為ResponseEntity<byte[]>才可以正常顯示,否則會導致服務之間的調用亂碼。

示例如下:

@PostMapping("/order/{productId}") ResponseEntity<byte[]> addCart(@PathVariable("productId") Long productId); 

作用在所有Feign Client上的配置方式

方式一:通過java bean 的方式指定。

@EnableFeignClients注解上有個defaultConfiguration屬性,可以指定默認Feign Client的一些配置。

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class) @EnableDiscoveryClient @SpringBootApplication @EnableCircuitBreaker public class ProductApplication { public static void main(String[] args) { SpringApplication.run(ProductApplication.class, args); } } 

DefaultFeignConfiguration內容:

@Configuration public class DefaultFeignConfiguration { @Bean public Retryer feignRetryer() { return new Retryer.Default(1000,3000,3); } } 

方式二:通過配置文件方式指定。

feign: client: config: default: connectTimeout: 5000 #連接超時 readTimeout: 5000 #讀取超時 loggerLevel: basic #日志等級 

Feign Client開啟日志

日志配置和上述配置相同,也有兩種方式。

方式一:通過java bean的方式指定

@Configuration public class DefaultFeignConfiguration { @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.BASIC; } } 

方式二:通過配置文件指定

logging: level: com.xt.open.jmall.product.remote.feignclients.CartFeignClient: debug 

Feign 的GET的多參數傳遞

目前,feign不支持GET請求直接傳遞POJO對象的,目前解決方法如下:

  1. 把POJO拆散城一個一個單獨的屬性放在方法參數中
  2. 把方法參數編程Map傳遞
  3. 使用GET傳遞@RequestBody,但此方式違反restful風格

介紹一個最佳實踐,通過feign的攔截器來實現。

@Component @Slf4j public class FeignCustomRequestInteceptor implements RequestInterceptor { @Autowired private ObjectMapper objectMapper; @Override public void apply(RequestTemplate template) { if (HttpMethod.GET.toString() == template.method() && template.body() != null) { //feign 不支持GET方法傳輸POJO 轉換成json,再換成query try { Map<String, Collection<String>> map = objectMapper.readValue(template.bodyTemplate(), new TypeReference<Map<String, Collection<String>>>() { }); template.body(null); template.queries(map); } catch (IOException e) { log.error("cause exception", e); } } } 

源碼分析請見文章微服務實戰SpringCloud之Feign源碼分析

  

作者:wangxiaowu241
鏈接:https://www.jianshu.com/p/8bca50cb11d8
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM