## 目錄 - [Feign 和OpenFeign](#Feign-和OpenFeign) - [Feign](#Feign) - [OpenFeign](#OpenFeign) - [openFeign的優勢](#openFeign的優勢) - [OpenFeign應用](#OpenFeign應用 ...
目錄
Feign 和OpenFeign
Feign
Feign是Spring Cloud組件中的一個輕量級RESTful的HTTP服務客戶端Feign內置了Ribbon,用來做客戶端負載均衡,去調用服務註冊中心的服務。
Feign的使用方式是:使用Feign的註解定義介面,調用這個介面,就可以調用服務註冊中心的服務Feign本身不支持Spring MVC的註解,它有一套自己的註解
OpenFeign
OpenFeign是Spring Cloud 在Feign的基礎上支持了Spring MVC的註解,如@RequesMapping等,是一個輕量級的Http封裝工具對象,大大簡化了Http請求,使得我們對服務的調用轉換成了對本地介面方法的調用。
OpenFeign 的 @FeignClient 可以解析SpringMVC的 @RequestMapping註解下的介面,並通過動態代理的方式產生實現類,實現類中做負載均衡並調用其他服務
openFeign的優勢
- 集成了Ribbon的負載均衡功能
- 集成Hystrix的熔斷器功能
- 支持請求壓縮
- 大大簡化了遠程調用的代碼,同時功能還增強啦
- 以更加優雅的方式編寫遠程調用代碼,並簡化重覆代碼
OpenFeign應用
1. 導入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
2. 使用
創建Feign介面
@FeignClient(value = "xx-template-service")//value = "xx-template-service"指定服務的名字
public interface DriverFeign {
/**
* demo feign 介面
*/
@PutMapping(value = "/driver/status")
Driver status(String id, Integer status);
}
Feign會通過動態代理,幫我們生成實現類。
註解@FeignClient聲明Feign的客戶端,註解value指明服務名稱介面定義的方法,採用SpringMVC的註解。Feign會根據註解幫我們生成URL地址
FeignClient 註解參數
- name/value:指定FeignClient的名稱,如果項目使用了Ribbon,name屬性會作為微服務的名稱,用於服務發現
- contextId:指定beanID
- url: url一般用於調試,可以手動指定@FeignClient調用的地址
- decode404:當發生http 404錯誤時,如果該欄位位true,會調用decoder進行解碼,否則拋出FeignException
- configuration: Feign配置類,可以自定義Feign的Encoder、Decoder、LogLevel、Contract
- fallback: 定義容錯的處理類,當調用遠程介面失敗或超時時,會調用對應介面的容錯邏輯,fallback指定的類必須實現@FeignClient標記的介面
- fallbackFactory: 工廠類,用於生成fallback類示例,通過這個屬性我們可以實現每個介面通用的容錯邏輯,減少重覆的代碼
- path: 定義當前FeignClient的統一首碼
註意事項
- 在使用fallback、fallbackFactory屬性時,需要使用@Component註解,保證fallback類被Spring容器掃描到
- 在使用FeignClient時,Spring會按name創建不同的ApplicationContext,通過不同的Context來隔離FeignClient的配置信息, 在使用配置類時,不能把配置類放到Spring App Component scan的路徑下,否則,配置類會對所有FeignClient生效.
啟用OpenFeign
我們需要在服務的啟動類上開啟 OpenFeign ,只需要在 **Application 啟動類上添加 @EnableFeignClients即可。
3. 日誌配置
logging:
level:
xx.template.cloud.web.service.feign.ServiceServiceFeign: debug
通過loggin.level.xx=debug來設置日誌級別。然而這個對Feign客戶端不會產生效果。因為@FeignClient註解修飾的客戶端在被代理時,都會創建一個新的Feign.Logger實例。我們需要額外指定這個日誌的級別才可以。
/**
* feign 日誌級別配置
* @return
*/
@Bean
public Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
或者在配置文件中配置
feign:
client:
config:
# 全局配置
default:
loggerLevel: full
Feign支持4種級別:
NONE:不記錄任何日誌,預設值
BASIC:僅記錄請求的方法,URL以及響應狀態碼和執行時間
HEADERS:在BASIC基礎上,額外記錄了請求和響應的頭信息
FULL:記錄所有請求和響應的明細,包括頭信息、請求體、元數據
4. 數據壓縮
用戶在網路請求過程中,如果網路不佳、傳輸數據過大,會造成體驗差的問題,我們需要將傳輸數據壓縮提升體驗。SpringCloud OpenFeign支持對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。
在客戶端中配置數據壓縮
feign:
compression:
request:
enabled: true # 開啟請求壓縮
response:
enabled: true # 開啟響應壓縮
也可以對請求的數據類型,以及觸發壓縮的大小下限進行設置
feign:
compression:
request:
enabled: true # 開啟請求壓縮
mime-types: text/html,application/xml,application/json # 設置壓縮的數據類型
min-request-size: 2048 # 設置觸發壓縮的大小下限
#以上數據類型,壓縮大小下限均為預設值
response:
enabled: true # 開啟響應壓縮
OpenFeign高級應用
OpenFeign熔斷降級的兩種方式-降級方法和降級工廠
首先在配置文件中配置如下,開啟熔斷降級
feign.hystrix.enabled=true
-
Feign 定義降級方法
feign介面定義
/** * FeignClient 註解的 fallback 屬性指定降級類 */ @FeignClient(name = "xx-template-cloud-service", fallback = ServiceServiceFeignFallBack.class) public interface ServiceServiceFeign { @GetMapping("/order/getOrder") Result getOrder(String id); }
feign介面降級方法定義
@Slf4j @Component class ServiceServiceFeignFallBack implements ServiceServiceFeign { @Override public Result getOrder(String id) { log.error("Feign介面熔斷 熔斷方式:ServiceServiceFeignFallBack"); return Result.error("500", "Feign介面熔斷"); } }
-
Feign 定義降級工廠
feign介面定義
/** * FeignClient 註解的 fallback 屬性指定降級類 */ @FeignClient(name = "xx-template-cloud-service", fallback = UserCenterFeignClientFallbackFactory.class) public interface ServiceServiceFeign { @GetMapping("/order/getOrder") Result getOrder(String id); }
feign介面降級工廠定義
@Component @Slf4j class UserCenterFeignClientFallbackFactory implements FallbackFactory<ServiceServiceFeign> { @Override public ServiceServiceFeign create(Throwable cause) { return new ServiceServiceFeign() { @Override public Result getOrder(String id) { log.error("Feign介面熔斷,熔斷方式:UserCenterFeignClientFallbackFactory"); return Result.error("500", "Feign介面熔斷"); } }; } }
踩坑指南
坑一:Http Client
OpenFeign預設使用jdk自帶的HttpURLConnection,我們知道HttpURLConnection沒有連接池、性能和效率比較低,如果採用預設,很可能會遇到性能問題導致系統故障
-
可以採用Apache HttpClient
註意feign-httpclient的版本,如果和Open Feign 不相容可能會報original request is required異常
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>10.10.1</version> </dependency>
feign.httpclient.enabled=true
-
也可以採用OkHttpClient
<dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> <version>10.2.0</version> </dependency>
feign.okhttp.enabled=true
ribbon中的Http Client
通過OpenFeign作為註冊中心的客戶端時,預設使用Ribbon做負載均衡,Ribbon預設也是用jdk自帶的HttpURLConnection,需要給Ribbon也設置一個Http client,比如使用okhttp,在properties文件中增加下麵配置
ribbon.okhttp.enabled=true
坑二:全局超時時間
OpenFeign可以設置超時時間,簡單粗暴,設置一個全局的超時時間。如果不配置超時時間,預設是連接超時10s,讀超時60s。
feign.client.config.default.connectTimeout=2000
feign.client.config.default.readTimeout=60000
但是如果某個服務的併發量很高,服務的超時時間過長會導致占用大量連接資源,導致系統崩潰,要防止這樣的故障發生,最好的做法就是給服務單獨設置超時時間。
當然,如果開啟熔斷後,不配置hystrix和ribbon的超時時間,那麼Hystrix與ribbon的預設請求超時時間都是1秒,建議配置Hystrix的超時時間要大於ribbon的超時時間,否則會在介面調用還未完成的時候直接進入回調方法。
# 開啟熔斷
feign.hystrix.enabled: true
坑三:單服務設置超時時間
上文所述,對單個服務設置超時時間。
feign.client.config.serviceA.connectTimeout=2000
feign.client.config.serviceA.readTimeout=60000
但是如果serviceA中有多個介面,其中X介面又調用serviceB的Feign介面,為了保證X介面請求不被單服務超時時間影響,需要單獨對X介面設置超時時間。
幾個組件的關係
hystrix+ribbon。hystrix在最外層,然後再到Ribbon,最後裡面的是http請求。所以說。hystrix的熔斷時間必須大於ribbon的 ( ConnectTimeout + ReadTimeout )。而如果ribbon開啟了重試機制,還需要乘以對應的重試次數(註意這裡的重試可以是ribbon的重試也可能是feign的重試),保證在Ribbon里的請求還沒結束時,Hystrix的熔斷時間不會超時。
遇到的問題
1. 使用Spring MVC註解,但請求方式不正確
- 參數沒有通過註解指定,此時的參數會自動封裝到body中,Feign檢測到body裡面有請求參數就會預設使用POST請求。
- Feign預設使用的是POST方法,如果想使用GET方法,需要在配置文件中設置spring.cloud.openfeign.client.config.default.method.name=get。
- 如果使用了Spring Cloud Netflix Feign,那麼可能是因為您的應用程式使用了Spring Cloud Netflix Ribbon來處理HTTP負載均衡。在這種情況下,您需要確保Ribbon的配置正確,並且不會影響Feign的GET請求。
2. 使用nacos做註冊中心,Feign調用時拉取的服務列表為空
我用的spring cloud版本是Hoxton.SR8
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
原因是Feign 在spring cloud Hoxton.M2版本之後,不再使用ribbon,所以我們在pom文件中還需要導入loadbalancer依賴,併排除掉nacos的ribbon依賴。
<!--nacos-discovery-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2021.1</version>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>
併在配置文件中添加
spring.cloud.loadbalancer.ribbon.enabled: false