1. 程式人生 > >Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 實踐

Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 實踐

# Spring Cloud Feign 自定義配置(重試、攔截與錯誤碼處理) 實踐 [TOC] > 本篇文章主要分享下 Feign 自定義配置的工程實踐,希望你們可以找到些有用的東西,文章已被[公眾號](https://mp.weixin.qq.com/s/lHAMhbrBo5mIkQbitSyfLw)收錄 基於 spring-boot-starter-parent 2.1.9.RELEASE, spring-cloud-openfeign 2.1.3.RELEASE ## 引子 Feign 是一個宣告式、模板化的HTTP客戶端,簡化了系統發起Http請求。建立它時,只需要建立一個介面,然後加上FeignClient註解,使用它時,就像呼叫本地方法一樣,作為開發者的我們完全感知不到這是在呼叫遠端的方法,也感知不到背後發起了HTTP請求: ```Java /** * @author axin * @suammry xx 客戶端 */ @FeignClient(value = "xxClient",url = "${xx.host:www.axin.com}") public interface DemoClient { @PostMapping(value = "/xxx/url", headers = "Content-Type=application/json"}) yourResponse requestHTTP(@RequestBody JSONObject param); } ``` 上述的程式碼就是一個定義一個Feign HTTP 客戶端,在其他類中只需要 @Autowired DemoClient,就可以像呼叫本地方法一樣發起HTTP請求。 介紹就到這,接下來進入主題,因為 FeignClient 將發起HTTP請求與解析返回報文都做了包裝,如果你的業務場景需要定製一些呼叫機制,比如: 1. 我想在發起請求響應超時失敗時自動重試 —— 自定義重試機制 2. 我想單獨對某些異常的HTTP狀態碼特殊處理 —— 自定義ErrorDecoder 3. 服務端介面需要驗證簽名,所以我方在發起請求時要生成簽名然後傳過去 —— 定義 Fegin 攔截器 基於此,本文就以上述3個需求場景為例來介紹如何自定義 FeignClient 的配置 ## FeignClient的預設配置類 > Feign Client 預設的配置類為 FeignClientsConfiguration, 這個類在 spring-cloud-netflix-core 的 jar 包下。 預設注入了很多 Feign 相關的配置Bean,包括FeignRetryer、 FeignLoggerFactory 和 FormattingConversionService 等。另外,Decoder、Encoder和 Contract 這3個類在沒有Bean被注入的情況下,會自動注入預設配置的 Bean,即ResponseEntity Decoder、SpringEncoder 和 SpringMvcContract。 如果你不知道如何自己定義配置時,不放點進去看看人家預設配置是如何實現的。這裡就不晒原始碼了。 ## FeignClient 註解引數 每個註解引數都做了註釋,詳情請見下方原始碼: ```Java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface FeignClient { /** * 指定FeignClient的名稱,如果專案使用了Ribbon,name屬性會作為微服務的名稱,用於服務發現 */ @AliasFor("name") String value() default ""; @Deprecated String serviceId() default ""; @AliasFor("value") String name() default ""; /** * Sets the @Qualifier
value for the feign client. * 這個bean在應用上下文中的名字為介面的全限定名,你也可以使用 @FeignClient 註解中的 qualifier 屬性給bean指定一個別名 */ String qualifier() default ""; /** * url地址 */ String url() default ""; /** * 當發生404錯誤,如果該欄位為true,會呼叫decoder進行解碼,否則丟擲FeignException */ boolean decode404() default false; /** * 指定FeignClient 的配置類,優先順序最高,預設的配置類為 FeignClientsConfiguration類 */ Class[] configuration() default {}; /** * 配置熔斷器的處理類 */ Class fallback() default void.class; /** * 工廠類,用於生產fallback類示例,通過這個屬性我們可以實現每個介面通用的容錯邏輯,減少重複程式碼 */ Class fallbackFactory() default void.class; /** * 定義統一的路徑字首 */ String path() default ""; /** * Whether to mark the feign proxy as a primary bean. Defaults to true. */ boolean primary() default true; } ``` ## 自定義Feign配置類 在 Spring Cloud 中,你可以通過 @FeignClient 註解宣告額外的配置(比 FeignClientsConfiguration 級別高)去控制feign客戶端,以一開始的feign介面為例: ```Java /** * @author axin * @suammry xx 客戶端 */ @FeignClient(value = "xxClient",url = "${xx.host:www.axin.com}",configuration = MyConfiguration.class) public interface DemoClient { @PostMapping(value = "/xxx/url", headers = "Content-Type=application/json"}) yourResponse requestHTTP(@RequestBody JSONObject param); } ``` 在上面這個示例中,feign客戶端在MyConfiguration中的配置將會覆蓋FeignClientsConfiguration中的配置。 **要注意的是**: MyConfiguration不需要使用@Configuration註解。如果加上了,它將**全域性生效**。 ### Retryer-重試機制的自定義 ```Java /** * @author axin * @summary fegin 客戶端的自定義配置 */ public class MyConfiguration { /** * 自定義重試機制 * @return */ @Bean public Retryer feignRetryer() { //fegin提供的預設實現,最大請求次數為5,初始間隔時間為100ms,下次間隔時間1.5倍遞增,重試間最大間隔時間為1s, return new Retryer.Default(); } } ``` ### ErrorDecoder-錯誤解碼器的自定義 > 當feign呼叫返回HTTP報文時,會觸發這個方法,方法內可以獲得HTTP狀態碼,可以用來定製一些處理邏輯等等。 ```Java /** * @author axin * @summary fegin 客戶端的自定義配置 */ @Slf4j public class MyConfiguration { /** * 自定義重試機制 * @return */ @Bean public Retryer feignRetryer() { //最大請求次數為5,初始間隔時間為100ms,下次間隔時間1.5倍遞增,重試間最大間隔時間為1s, return new Retryer.Default(); } @Bean public ErrorDecoder feignError() { return (key, response) -> { if (response.status() == 400) { log.error("請求xxx服務400引數錯誤,返回:{}", response.body()); } if (response.status() == 409) { log.error("請求xxx服務409異常,返回:{}", response.body()); } if (response.status() == 404) { log.error("請求xxx服務404異常,返回:{}", response.body()); } // 其他異常交給Default去解碼處理 // 這裡使用單例即可,Default不用每次都去new return new ErrorDecoder.Default().decode(key, response); }; } } ``` 採用了lambda的寫法,response變數是Response型別,通過status()方法可以拿到返回的HTTP狀態碼,body()可以獲得到響應報文。 ### Feign攔截器實踐 > 攔截器在請求發出之前執行,在攔截器程式碼裡可以修改請求引數,header等等,如果你有簽名生成的需求,可以放在攔截器中來實現 ```Java /** * @author axin * @summary fegin 客戶端的自定義配置 */ @Slf4j public class MyConfiguration { /** * 自定義重試機制 * @return */ @Bean public Retryer feignRetryer() { //最大請求次數為5,初始間隔時間為100ms,下次間隔時間1.5倍遞增,重試間最大間隔時間為1s, return new Retryer.Default(); } @Bean public ErrorDecoder feignError() { return (key, response) -> { if (response.status() == 400) { log.error("請求xxx服務400引數錯誤,返回:{}", response.body()); } if (response.status() == 409) { log.error("請求xxx服務409異常,返回:{}", response.body()); } if (response.status() == 404) { log.error("請求xxx服務404異常,返回:{}", response.body()); } // 其他異常交給Default去解碼處理 // 這裡使用單例即可,Default不用每次都去new return new ErrorDecoder.Default().decode(key, response); }; } /** * fegin 攔截器 * @return */ @Bean public RequestInterceptor cameraSign() { return template -> { // 如果是get請求 if (template.method().equals(Request.HttpMethod.GET.name())) { //獲取到get請求的引數 Map> queries = template.queries(); } //如果是Post請求 if (template.method().equals(Request.HttpMethod.POST.name())) { //獲得請求body String body = template.requestBody().asString(); JSONPObject request = JSON.parseObject(body, JSONPObject.class); } //Do what you want... 例如生成介面簽名 String sign = "根據請求引數生成的簽名"; //放入url?之後 template.query("sign", sign); //放入請求body中 String newBody = "原有body" + sign; template.body(Request.Body.encoded(newBody.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8)); }; } } ``` 可以看到程式碼中給瞭如何獲取請求引數和修改請求引數的示例。 ## 總結 小結一下,對於開頭提出的場景: 1. 我想在發起請求響應超時失敗時自動重試 —— 自定義重試機制 2. 我想單獨對某些異常的HTTP狀態碼特殊處理 —— 自定義ErrorDecoder 3. 服務端介面需要驗證簽名,所以我方在發起請求時要生成簽名然後傳過去 —— 定義 Fegin 攔截器 給出了自定義 feign 配置的方式實現的樣例程式碼,希望對你有用,如果有更好的方式簡化HTTP請求,歡迎留言分享~ ## 參考連結 - [重新定義Spring Cloud實戰](https://book.douban.com/subject/30338647/) - [Spring Cloud Netflix官方文件](http://ifeve.com/%E3%80%8Aspring-cloud-netflix%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3%E3%80%8B7-%E5%A3%B0%E6%98%8E%E5%BC%8F-rest-%E5%AE%A2%E6%88%B7%E7%AB%AF%EF%BC%9A-