OpenFeign
文章目錄
- OpenFeign
- 一. 什么是OpenFeign
- 二. OpenFeign基礎使用
- 1.添加依賴
- 2.配置Nacos配置信息
- 3.在項目中開啟OpenFeign
- 4.編寫OpenFeign調用代碼
- 5.調用OpenFeign接口
- 三. OpenFeign內置的超時重試機制
- 1.配置超時重試
- 2.覆蓋Retryer對象
- 四.自定義超時重試機制
- 1.自定義超時重試類
- 2.設置配置文件
- 五.OpenFeign超時重試底層實現
- 1.超時重試底層實現
一. 什么是OpenFeign
OpenFeign的全稱為Spring Cloud OpenFeign,是Spring Cloud 開發的一款基于Feign的框架,聲明式Web服務客戶端。
Feign 是Netflix開源的一個聲明式的Web服務客戶端,它簡化了基于HTTP的服務調用,使得服務間的通信變得更加簡單和靈活。Feign通過定義接口、注解和動態代理等方式,將服務調用的過程封裝起來,開發者只需定義服務接口,而無需關心底層的HTTP請求和序列化等細節。
OpenFeign功能升級
OpenFeign在Feign的基礎上提供了以下增強和擴展功能:
- 更好的集成Spring Cloud組件:OpenFeign與Spring Cloud其他組件緊密集成,可以無縫地與其他Spring Cloud組件一起使用。
- 支持@FeignClient注解:OpenFeign引入了@FeignClient注解作為Feign客戶端的標識,可以方便地定義和使用遠程服務的聲明式接口。
- 錯誤處理改進:OpenFeign對異常的處理做了增強,提供了更好的錯誤信息和異常處理機制,使得開發者可以更方便地進行錯誤處理。
- 更豐富的配置項:OpenFeign提供了豐富的配置選項,可以對Feign客戶端的行為進行靈活的配置,例如超時設置、重試策略等。
二. OpenFeign基礎使用
OpenFeign通常要配合注冊中心一起使用,并且新版本OpenFeign也必須和負載均衡器一起使用,使用步驟如下:
- 添加依賴(Nacos注冊中心、OpenFeign、Spring Cloud LoadBalancer)
- 配置Nacos服務端信息
- 在項目中開啟OpenFeign
- 編寫OpenFeign調用代碼
- 編寫代碼通過OpenFeign調用生產者
1.添加依賴
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2.配置Nacos配置信息
spring:application:name: nacos-consumer-democloud:nacos:discovery:server-addr: localhost:8848username: nacospassword: nacosregister-enabled: false # 消費者(不需要注冊到nacos中)
3.在項目中開啟OpenFeign
在啟動類文件中添加@EnableFeignClients
注解即可
4.編寫OpenFeign調用代碼
@Service
@FeignClient("nacos-discovery") // 表示調用 nacos 中的 nacos-discovery 服務
public interface UserService {@RequestMapping("/user/getnamebyid") // 調用生產者的"/user/getnamebyid"接口public String getNameById(@RequestParam("id") int id);}
5.調用OpenFeign接口
@RestController
public class BusinessController {@Autowiredprivate UserService userService;@RequestMapping("/getnamebyid")public String getNameById(Integer id){System.out.println("------- do provider getNameById method" +LocalDateTime.now());return userService.getNameById(id);}}
三. OpenFeign內置的超時重試機制
在微服務架構中,服務之間是通過網絡進行通信的,而網絡是復雜和不穩定的,所以在調用服務時可能會失敗或超時,那么在這種情況下,就需要給OpenFeign配置超時重試機制。
什么是超時重試?
超時重試是一種在網絡通信中常用發的策略,用于處理請求在一定時間內未能得到響應的情況。當發起請求后,如果規定時間內沒有得到預期的響應,就會觸發超時重試機制,重新發送請求。
超時重試的主要目的是提高請求的可靠性和穩定性,以應對網絡的不穩定、服務不可用、響應延遲等不確定因素。
OpenFeign默認是不自動開啟超時重試
開啟有以下步驟:
- 配置超時重試
- 覆蓋Retryer對象
1.配置超時重試
spring:cloud: openfeign:client:config:default:connect-timeout: 1000 #連接超時時間read-timeout: 1000 #讀取超時時間
2.覆蓋Retryer對象
@Configuration
public class RetryerConfig {@Beanpublic Retryer retryer(){return new Retryer.Default(1000,//重試間隔時間1000,//最大重試間隔時間3);//最大重試次數}
}
最大重試次數為3次,最大重試間隔時間是1秒,重試間隔時間是1秒
這時我們啟動一個實例,并設置保護閾值為0,啟動消費者。
訪問服務
此時服務無法訪問,并觸發了超時重試機制,這時打開生產者者的控制臺:
在控制臺我們可以看到總共打印了3次日志,因為我們設置的最大重試次數是3
為什么不是4次呢?
為什么不是4次?
因為Retryer的Default方法的源碼中重試次數變量attempt是從1開始的,然后核心方法continueOrPropagate中的if判斷是當this.attempt++ >= this.maxAttempts 時,才拋出異常。
四.自定義超時重試機制
自定義超時重試機制實現為以下兩步:
- 自定義超時重試類(實現Retryer接口,并重寫continueOrPropagate方法)
- 設置配置文件
1.自定義超時重試類
常見的超時重試策略有以下三種:
- 固定間隔重試:每次重試之間的時間間隔固定不變。
- 指數間隔重試:每次重試之間的時間間隔按指數遞增。
- 隨機間隔重試:每次重試之間的時間間隔是隨機的。
public class CustomRetryer implements Retryer {private final int maxAttempts; //最大嘗試次數private final long backoff; //重試間隔時間int attempt; //當前重試次數public CustomRetryer() {this.maxAttempts=3;this.backoff =1000;this.attempt=0;}public CustomRetryer(int maxAttempts, long backoff) {this.maxAttempts = maxAttempts;this.backoff = backoff;this.attempt=0;}@Overridepublic void continueOrPropagate(RetryableException e) {if (attempt++>=maxAttempts){throw e;}long interval = this.backoff;//重試間隔時間System.out.println(LocalDateTime.now()+" | 執行一次重試:"+interval);try {//重試間隔實際Thread.sleep(interval*attempt);} catch (InterruptedException ex) {ex.printStackTrace();}}@Overridepublic Retryer clone() {return new CustomRetryer(maxAttempts,backoff);}
}
2.設置配置文件
spring:cloud:openfeign:client:config:default:connect-timeout: 1000 #連接超時時間read-timeout: 1000 #讀取超時時間retryer: com.example.consumer.config.CustomRetryer #自定義失敗重試類
啟動生產者和消費者服務,并嘗試調用服務,并查看控制臺
這里我們設定的重試次數是3,但為什么會打印4次呢?
是因為在自定義重試類中的attempt變量是從0開始的。
觀察日志文檔的時間間隔:從2s->3s->4s,最初attempt為1,1*1+read-timeout的1s所以是2s,然后1*1+read-timeout,以此類推……
五.OpenFeign超時重試底層實現
首先我們先了解以下OpenFeign的底層實現邏輯
- 加注解:在啟動類或配置類上添加
@EnableFeignClients
注解 - 動態代理:這個注解會觸發Spring框架的自動配置機制,掃描所有標記的
@FeignClient
的接口,并為它們創建代理實例。 - RequestTemplate發送HTTP請求:OpenFeign不能直接發送HTTP請求,它在動態代理里面將注解的路由地址拿出來,然后就能拼出來一個URL請求地址,然后再使用RequestTemplate去發送HTTP請求。
- RestTemplate依靠HTTP框架實現web請求:RestTemplate只是一個模板方法類,它只是規定了一個調用的API,底層并沒有實現,依靠的是HTTP框架實現的web請求(Apache 的HttpClient框架)
1.超時重試底層實現
OpenFeign超時的底層實現是通過配置底層的HTTP客戶端來實現的。OpenFeign允許在請求連接和讀取數據階段設置超時時間,具體超時配置可以通過HTTP客戶端的連接超時(connectTimeout)和讀取超時(readTimeout)來實現,可以在配置文件中設置超時參數。
OpenFeign重試的底層可通過觀察源碼來了解,它的源碼在SynchronousMethodHandler的invoke方法下,如下所示:
public Object invoke(Object[] argv) throws Throwable {RequestTemplate template = this.buildTemplateFromArgs.create(argv);Request.Options options = this.findOptions(argv);Retryer retryer = this.retryer.clone();// 死循環,如果成功或者重試結束就返回(通過throw終止while循環)while(true) {try {//通過HTTP Client發起通信return this.executeAndDecode(template, options);} catch (RetryableException var9) {RetryableException e = var9;//判斷是否重試try {retryer.continueOrPropagate(e);} catch (RetryableException var8) {Throwable cause = var8.getCause();if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {throw cause;}throw var8;}if (this.logLevel != Level.NONE) {this.logger.logRetry(this.metadata.configKey(), this.logLevel);}}}
}
因此OpenFeign的重試功能是通過其內置的Retryer組件和底層的HTTP客戶端實現的。
Retryer組件提供了重試策略的邏輯實現,而遠程接口則通過HTTP客戶端來完成調用。