Spring Feign教程大全

banq發表於2024-04-23

在微服務架構的世界中,服務之間的通訊至關重要,開發人員通常依賴強大的工具來促進互動。在這些工具中,Feign 已成為流行的選擇,它提供宣告式 HTTP 客戶端,以便更輕鬆地使用 RESTful 服務。

什麼是Feign?
 Feign 是 Netflix 開發的宣告式 Web 服務客戶端,旨在簡化編寫 HTTP 客戶端的過程。它允許開發人員直接在 Java 介面中將 HTTP 請求編寫為註釋,從而抽象出 HTTP 通訊的複雜性。

Feign 的主要優點:

  1. 宣告式方法:使用Feign,開發人員可以使用註釋來定義HTTP客戶端介面,這大大減少了樣板程式碼,使程式碼庫更易於維護。
  2. 負載均衡: Feign與Spring Cloud Load Balancer無縫整合,實現跨服務例項的自動負載均衡。
  3. 錯誤處理: Feign提供了健壯的錯誤處理機制,允許開發者輕鬆定義自定義的錯誤處理邏輯。
  4. 攔截器: Feign支援攔截器,可用於日誌記錄、身份驗證或自定義標頭操作等任務。

當談到在基於 Spring 的應用程式中使用 Feign 時,開發人員面臨兩個主要選擇:Spring Cloud Feign 和 OpenFeign。雖然它們具有共同的基礎,但兩者之間存在顯著差異,各自具有獨特的優勢和用例。

Spring Cloud Feign
Spring Cloud Feign 是流行的 Spring 框架的擴充套件,專為構建雲原生應用程式而設計。它與 Spring 生態系統的其他元件無縫整合,為開發人員提供了一個用於開發微服務的有凝聚力的環境。以下是 Spring Cloud Feign 的一些主要特性和差異:

1. 原生Spring整合:也許Spring Cloud Feign最顯著的優勢是它與Spring生態系統的緊密整合。它利用了熟悉的 Spring 概念,例如依賴注入、AOP 和配置管理,使其成為基於 Spring 的應用程式的自然選擇。

2.與Eureka整合: Spring Cloud Feign與服務註冊和發現工具Eureka無縫協作。這種整合允許自動服務發現和負載平衡,從而簡化了動態環境中使用服務的過程。

3. Ribbon負載均衡器: Spring Cloud Feign與客戶端負載均衡庫Ribbon整合。這允許開發人員在服務的多個例項之間分配流量,從而提高容錯能力和可擴充套件性。

4. Hystrix 斷路器整合: Spring Cloud Feign 的另一個顯著特性是它與斷路器庫 Hystrix 的整合。這種整合透過優雅地處理故障並防止跨服務的級聯故障來提供容錯和恢復能力。

5.簡化的配置: Spring Cloud Feign利用Spring Boot的自動配置功能提供簡化的配置選項。開發人員可以使用屬性或註釋輕鬆定製 Feign 客戶端,減少樣板程式碼並提高可維護性。

OpenFeign:
另一方面,OpenFeign 是一個獨立專案,最初是 Netflix Feign 客戶端的一個分支。雖然它與 Spring Cloud Feign 有許多相似之處,但它與 Spring 生態系統的耦合並不緊密。以下是 OpenFeign 的一些顯著特徵:

1. 與框架無關:與 Spring Cloud Feign 不同,OpenFeign 不依賴於 Spring 生態系統。對於可能使用 Spring 以外框架(例如 Micronaut 或 Quarkus)的開發人員來說,這使其成為更通用的選擇。

2.宣告式API: OpenFeign保留了Feign的宣告式API方法,允許開發人員在Java介面中使用註釋來定義HTTP客戶端。這簡化了編寫和維護 HTTP 客戶端的過程,促進了乾淨簡潔的程式碼庫。

3.可插拔架構: OpenFeign具有模組化和可擴充套件的架構,允許開發人員根據需要插入自定義元件和攔截器。這種靈活性支援高階定製以及與各種第三方庫的整合。

4. 對反應式程式設計的支援: Spring Cloud Feign 主要關注傳統的阻塞 I/O,而 OpenFeign 提供對反應式程式設計模型的支援,例如 Project Reactor 和 RxJava。這使得它成為基於響應式原則構建的應用程式的合適選擇。

5.減少依賴開銷:由於OpenFeign與Spring耦合不緊密,因此與Spring Cloud Feign相比,它的依賴關係更少。這可以減少工件大小並提高資源受限環境中的效能。

選擇正確的選項:
在選擇 Spring Cloud Feign 和 OpenFeign 時,開發人員應該考慮他們的具體需求、現有技術堆疊和架構偏好。以下是一些需要考慮的因素:

  • - Spring生態系統依賴:如果您的專案嚴重依賴Spring生態系統並使用Eureka和Ribbon等元件,那麼Spring Cloud Feign由於其無縫整合可能是首選。  
  • - 框架靈活性:如果您需要一個與框架更加無關的解決方案或計劃使用非 Spring 框架,OpenFeign 提供更大的靈活性以及與更廣泛框架的相容性。
  • - 反應式與阻塞式 I/O:考慮您的應用程式架構是否支援反應式程式設計模型還是傳統的阻塞式 I/O。雖然這兩個選項都支援非同步通訊,但 OpenFeign 可能更適合反應式應用程式。
  • - 定製需求:評估您的專案所需的定製級別和可擴充套件性。 OpenFeign 的模組化架構和可插拔元件為自定義行為和與第三方庫整合提供了更大的靈活性。

什麼是@EnableFeignClients?
在 Spring 生態系統中,可以透過“@EnableFeignClients”註釋將 Feign 無縫整合到您的應用程式中。

`@EnableFeignClients` 是一個 Spring 註解,用於在 Spring Boot 應用程式中啟用 Feign 客戶端支援。當應用於配置類時,它會掃描指定的包中是否有帶有“@FeignClient”註釋的介面,併為這些介面建立代理。

Feign 客戶端是基於介面的,這意味著您定義一個用“@FeignClient”註釋的介面來表示外部服務。 Feign 在執行時動態生成該介面的實現,透明地處理 HTTP 請求和響應。

為什麼使用@EnableFeignClients?
1. 抽象: `@EnableFeignClients` 抽象了 HTTP 通訊的複雜性,使開發人員能夠專注於定義服務介面,而不是處理低階 HTTP 客戶端實現。
2. 宣告式方法: Feign 鼓勵採用宣告式方式定義 RESTful 客戶端,開發人員使用註釋和方法簽名指定客戶端所需的行為。
3. 與其他 Spring 功能整合: `@EnableFeignClients` 與其他 Spring 功能無縫整合,例如依賴注入、配置管理和錯誤處理。
4. 負載均衡和熔斷: Feign 與 Ribbon 整合以實現客戶端負載均衡,並與 Hystrix 整合以實現熔斷,從而提供開箱即用的彈性和容錯能力。

何時使用@EnableFeignClients?
您應該考慮在以下場景中使用“@EnableFeignClients”:

  • - 微服務架構:當使用微服務架構構建應用程式時,服務之間需要進行通訊。
  • - RESTful API:與外部服務或其他微服務提供的 RESTful API 互動時。
  • - 減少樣板程式碼:當您想要減少發出 HTTP 請求和處理響應所需的樣板程式碼量時。

@EnableFeignClients 內部如何工作
在內部,@EnableFeignClients負責在 Spring 應用程式中設定 Feign 基礎設施。以下是其工作原理的高階概述:

  1. 掃描@FeignClient介面: `@EnableFeignClients`掃描指定的基礎包,查詢帶有`@FeignClient`註解的介面。
  2. 代理生成:對於找到的每個介面,Feign在執行時動態生成一個代理實現。該代理實現介面中定義的方法並處理 HTTP 通訊細節。
  3. 依賴注入: Spring 在應用程式中需要的地方注入這些代理例項,允許您像使用本地 bean 一樣使用它們。
  4. 與Ribbon和Hystrix整合:如果配置,Feign可以與Ribbon整合以實現客戶端負載平衡,並與Hystrix整合以實現熔斷。這為分散式系統提供了彈性和容錯能力。
  5.  自定義和配置: `@EnableFeignClients` 提供了透過屬性、配置類或 Spring Boot 啟動器提供的自定義來自定義 Feign 客戶端行為的選項。

使用Spring Cloud Feign 實現 HTTP 連線池
HTTP 連線池是一種用於減少建立和銷燬 HTTP 連線的開銷的技術。透過重用現有連線,它可以提高效能並減少服務間通訊的延遲。 Spring Boot 為 Apache HttpClient 提供內建支援,其中包括連線池功能。

在 Spring Boot 中配置 HTTP 連線池:

@Configuration
public class HttpClientConfig {

    @Bean
    public HttpClient httpClient() {
        return HttpClient.newBuilder()
                .connectTimeout(Duration.ofSeconds(10))
                .executor(Executors.newFixedThreadPool(10)) <font>// 為非同步請求配置執行緒池<i>
                .build();
    }

    @Bean
    public ClientHttpRequestFactory clientHttpRequestFactory(HttpClient httpClient) {
        return new HttpComponentsClientHttpRequestFactory(httpClient);
    }
}

將 Feign 與 HTTP 連線池整合:
要在 Feign 中利用 HTTP 連線池,我們可以將 Feign 配置為使用 Apache HttpClient 作為其底層 HTTP 客戶端。

@Configuration
public class FeignConfig {

    @Autowired
    private HttpClient client;

    @Bean
    public Client feignClient() {
        return new ApacheHttpClient(client);
    }
}

透過配置 Feign 使用 Apache HttpClient,我們確保 Feign 受益於 Apache HttpClient 提供的連線池功能。
效能最佳化的最佳實踐:

  1. 調整連線池大小:根據預期負載和可用資源調整連線池的大小。
  2. 設定連線超時:配置適當的連線超時值,防止執行緒無限期阻塞。
  3.  監控和調優:定期監控連線池指標並根據需要調整配置引數以最佳化效能。

Feign  vs. RestTemplate
Feign 相對於 RestTemplate 的優點:

  1. 宣告式 API: Feign 允許您使用註釋以宣告式方式定義 HTTP 客戶端繫結,與通常以程式設計方式配置 HTTP 請求的 RestTemplate 相比,這可以使您的程式碼庫更具可讀性和可維護性。
  2. 與 Spring Cloud 整合: Feign 與 Spring Cloud 整合,可以更輕鬆地在微服務架構中使用,特別是當您需要服務發現、負載均衡和斷路器等功能時。
  3. 契約優先的方法: Feign透過OpenAPI或Swagger支援契約優先的方法,允許您先定義API契約,然後生成客戶端程式碼。這可以使客戶端和伺服器團隊之間更好地保持一致,並確保遵守 API 合同。
  4. 客戶端負載平衡: Feign 支援開箱即用的客戶端負載平衡,這對於需要將請求分佈到服務的多個例項的分散式系統中非常有用。
  5. 與Hystrix整合: Feign與Hystrix無縫整合,用於實現斷路器和容錯模式,使構建彈性微服務變得更加容易。

Feign相對於RestTemplate的缺點:

  1. 學習曲線:雖然Feign簡化了定義HTTP客戶端的過程,但它引入了開發人員需要學習的一套自己的概念和註釋。 RestTemplate 是一種更傳統的方法,對於已經熟悉 HTTP 客戶端庫的開發人員來說,學習曲線可能較低。
  2. 靈活性: Feign 的宣告式方法可能無法提供與 RestTemplate 相同級別的靈活性,特別是當您需要執行復雜的請求配置或處理無法透過註釋輕鬆表達的邊緣情況時。
  3. 效能開銷:與直接使用 RestTemplate 相比,Feign 的抽象層可能會引入效能開銷,儘管在許多情況下,開銷可能可以忽略不計,並且會被提高開發人員生產力的好處所抵消。
  4. 社群採用: RestTemplate 長期以來一直是 Spring 生態系統的一部分,並且擁有大量社群採用。 Feign 雖然越來越受歡迎,但社群支援可能不夠廣泛,線上可用資源也較少。
  5. 相容性有限: Feign 與 Spring Cloud 緊密耦合,可能不適合不使用 Spring 生態元件的專案。 RestTemplate是一個更通用的HTTP客戶端,可以用於更廣泛的專案和框架。

在執行時動態更改 Feign URL
透過利用 Spring Cloud 對屬性解析和動態配置更新的支援,可以在執行時動態更改 Feign URL。實現此目的的一種方法是使用 Spring 的環境抽象以及 Spring Cloud Config Server 進行集中配置管理。

以下是關於如何在執行時為 Feign 客戶端實現動態 URL 切換的分步指南:

1.設定Spring Cloud配置伺服器:
   - 在您的環境中配置 Spring Cloud 配置伺服器。該伺服器將作為儲存和管理配置屬性的集中位置。
   - 確保您的微服務配置為從此伺服器獲取其配置。

2. 定義 Feign 客戶端配置屬性:
   - 在“application.yml”或“application.properties”檔案中定義 Feign 客戶端 URL 的屬性。

  feign:
       client:
         service1:
           url: http:<font>//service1-hostname:service1-port<i>
         service2:
           url: http:
//service2-hostname:service2-port<i>

3. 建立配置 Bean 來訪問屬性:
   - 在應用程式中建立一個配置 bean 以動態訪問 Feign 客戶端屬性。
   - 您可以使用Spring的`@Value`註釋或`Environment`抽象來訪問這些屬性。

@Component
     public class FeignConfig {
         @Autowired
         private Environment environment;
         
         public String getServiceUrl(String serviceName) {
             return environment.getProperty(<font>"feign.client." + serviceName + ".url");
         }
     }

4. 在執行時更新 Feign 客戶端:
   - 將 `FeignConfig` bean 注入到需要動態更改 URL 的 Feign 客戶端類中。
   - 呼叫“getServiceUrl”方法根據服務名稱檢索 URL。
   - 使用 setter 方法或建構函式注入在 Feign 客戶端中動態設定 URL。

 

@Component
     public class MyFeignClient {
         private FeignConfig feignConfig;
         private MyFeignClientInterface feignClient;
         
         @Autowired
         public MyFeignClient(FeignConfig feignConfig) {
             this.feignConfig = feignConfig;
             String serviceUrl = feignConfig.getServiceUrl(<font>"service1");
             this.feignClient = Feign.builder().target(MyFeignClientInterface.class, serviceUrl);
         }
         
         
// Other methods to use the feignClient...<i>
     }


5.重新整理配置:
   - 為確保在執行時獲取配置屬性的更改,您可以啟用 Spring Cloud Config 的重新整理機制。
   - 將 `@RefreshScope` 註解新增到需要在配置屬性更改時重新整理的 Bean 中。
   - 每當需要動態更新 Feign 客戶端 URL 時,使用 Spring Actuator 的 `/actuator/refresh` 端點觸發重新整理。
   - 例如

@RefreshScope
     @Component
     public class MyFeignClient { 
         <font>// Feign client implementation...<i>
     }

透過這種設定,您的 Feign 客戶端將能夠在執行時根據從 Spring Cloud Config Server 獲取的配置屬性動態切換 URL。這種方法可確保在微服務環境中為 Feign 客戶端處理 URL 更改時的靈活性和易管理性。

使用 @RestControllerAdvice 處理 Spring Boot 中的 Feign 異常
處理異常是開發健壯的微服務的一個重要方面,特別是在處理服務之間的通訊時。在 Spring Boot 應用程式中,Feign 客戶端通常用於向其他微服務發出 HTTP 請求。但是,當這些通訊嘗試期間發生錯誤時,必須妥善處理異常。 

@RestControllerAdvice 是 Spring Boot 中的一個強大功能,允許您為 Spring MVC 控制器定義全域性異常處理。透過利用@RestControllerAdvice,您可以集中異常處理邏輯並確保整個應用程式中錯誤響應的一致性。

1. 建立自定義異常類
我們將首先建立一個自定義異常類來表示特定於 Feign 客戶端的異常。此類將擴充套件 RuntimeException 或任何其他適當的超類。

public class FeignClientException extends RuntimeException {
    public FeignClientException(String message) {
        super(message);
    }
}


2. 編寫@RestControllerAdvice類
接下來,我們將建立一個用 @RestControllerAdvice 註解的類來全域性處理異常。該類將包含用 @ExceptionHandler 註解的方法來處理特定型別的異常,包括 FeignException。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.cloud.openfeign.FeignException;

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(FeignException.class)
    public ResponseEntity<String> handleFeignException(FeignException e) {
        <font>// 從 FeignException 中提取有用資訊<i>
        HttpStatus status = HttpStatus.resolve(e.status());
        String errorBody = e.getMessage();
// You may want to parse the response body for specific error details<i>
        
       
//記錄異常或執行其他必要操作<i>
        
       
// 返回帶有相應狀態程式碼和錯誤資訊的 ResponseEntity<i>
        return ResponseEntity.status(status != null ? status : HttpStatus.INTERNAL_SERVER_ERROR)
                             .body(errorBody != null ? errorBody :
"Feign Client Error");
    }

   
// Add more exception handlers as needed<i>
}


在這個例子中,handleFeignException方法使用@ExceptionHandler註解,專門處理FeignException。在此方法中,我們從異常中提取相關資訊,例如 HTTP 狀態程式碼和響應正文,並返回帶有錯誤詳細資訊的相應 ResponseEntity。

3.使用Feign客戶端
確保您在 Spring Boot 應用程式中使用 Feign 客戶端與其他微服務進行通訊。 Feign 客戶端是使用 @FeignClient 註解的介面,允許您向其他服務發出 HTTP 請求。

@FeignClient(name = <font>"example-service", url = "http://example.com")
public interface ExampleFeignClient {
   
// Define your Feign client methods here<i>
}

FeignContext - NoSuchBeanDefinitionException
當您在使用 @EnableFeignClients 和 @FeignClient 時遇到與 FeignContext 相關的 NoSuchBeanException 時,通常表明 Spring 找不到“FeignContext”型別的 bean 來注入需要的位置。發生這種情況的原因有多種。以下是排查和解決問題的分步指南:

1. 檢查依賴關係:確保您的專案中存在使用 Feign 所需的依賴關係。如果您在 Spring Cloud 應用程式中使用 Feign,那麼它通常會作為 Spring Cloud 依賴項的一部分包含在內。確保您的“pom.xml”或“build.gradle”檔案包含必要的依賴項。

2. 正確啟用 Feign Clients:確保您已使用“@EnableFeignClients”正確註釋您的主應用程式類。該註釋應該放在 Spring 在元件掃描期間掃描的配置類上。

 

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

3. Feign 客戶端介面:驗證您的 Feign 客戶端介面是否已正確定義。它應該是一個帶有`@FeignClient`註釋的介面,並且應該指定目標服務的名稱。

  @FeignClient(name = <font>"your-service-name")
   public interface YourFeignClient {
       
// Define your methods here<i>
   }


4. 元件掃描:確保 Spring 掃描包含 Feign 客戶端和主應用程式類的包。如果您的 Feign 客戶端介面沒有被掃描,Spring 將不會建立必要的 bean。

5. 檢查 Feign 上下文配置:如果您要自定義 Feign 的行為或使用自定義配置,請確保它們配置正確。如果您定義了自定義“FeignContext” bean,請確保它已正確初始化並且可用於注入。

6. 條件載入:如果您使用條件載入或配置檔案,請確保根據活動配置檔案建立必要的 Bean。

7.依賴問題:檢查Spring Cloud和Feign依賴是否存在衝突或版本不匹配。有時,不同版本的 Spring Cloud 和 Feign 可能不相容。

8. 重新啟動和全新構建:有時,由於過時的配置或依賴關係可能會出現問題。嘗試重新啟動您的應用程式並執行乾淨的構建。

透過系統地檢查每個步驟,您應該能夠識別並解決與 FeignContext 相關的“NoSuchBeanException”。

用 Feign 客戶端和 Micrometer 增強微服務中的 HTTP 呼叫監控
為什麼要監控 HTTP 呼叫?

在深入研究整合細節之前,我們先了解為什麼監控 HTTP 呼叫在微服務架構中至關重要。微服務透過 HTTP 請求相互通訊,形成依賴網路。監控這些呼叫可以讓您:
1. 跟蹤效能:監控響應時間和吞吐量,以識別瓶頸並最佳化服務效能。
2. 檢測錯誤和故障:捕獲錯誤率和狀態程式碼,以便及時檢測和解決問題,確保高可用性。
3. 獲得最佳化見解:收集有關資源使用情況、延遲和請求模式的指標,以便為擴充套件和資源分配做出明智的決策。

Micrometer 為各種監控系統提供了一個供應商中立的指標儀表外觀,使開發人員能夠輕鬆地使用自定義指標來儀表其應用程式。它為監控 JVM 指標、HTTP 請求、資料庫查詢等提供全面支援。

將 Feign 客戶端與 Micrometer 整合
要透過使用 Feign 客戶端和 Micrometer 來維持 HTTP 呼叫的範圍,您需要將 Feign 和 Micrometer 整合到您的應用程式中。以下是有關如何實現此目標的分步指南:

1.新增依賴:
   確保專案的構建檔案中具有必要的依賴項。這包括 Feign、Micrometer 以及應用程式所需的任何其他依賴項。

2. 配置Micrometer :
   設定 Micrometer 來監控應用程式的指標。這通常涉及配置指標登錄檔和選擇監控系統(例如 Prometheus、Graphite 等)。

3. 儀器假客戶端:
   您需要對 Feign 客戶端進行檢測以記錄指標。這可以透過建立自定義 Feign RequestInterceptor 或使用 Feign 內建的 Logger 或 ErrorDecoder 來實現。

4. 與跟蹤整合:
   如果您使用分散式跟蹤(例如,使用 OpenTelemetry、Jaeger 等),請確保您的 Feign 客戶端配置為傳播跟蹤上下文。這可確保 HTTP 呼叫的範圍在跟蹤中連結在一起。

import feign.Logger;
import feign.RequestInterceptor;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentracing.Tracer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignConfig {

    private final Tracer tracer; <font>// Assuming you have a Tracer bean<i>

    private final MeterRegistry meterRegistry;
// Assuming you have a MeterRegistry bean<i>

    public FeignConfig(Tracer tracer, MeterRegistry meterRegistry) {
        this.tracer = tracer;
        this.meterRegistry = meterRegistry;
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> {
           
// Propagate tracing context<i>
            tracer.inject(tracer.activeSpan().context(), io.opentracing.propagation.Format.Builtin.HTTP_HEADERS, new FeignRequestAdapter(template));

           
// Add Micrometer instrumentation<i>
            long start = System.nanoTime();
            template.header(
"startTimestamp", String.valueOf(start));
        };
    }

    @Bean
    public Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }

    @Bean
    public FeignErrorDecoder feignErrorDecoder() {
        return new FeignErrorDecoder(meterRegistry);
    }

    private static class FeignRequestAdapter implements TextMap {
        private final RequestTemplate requestTemplate;

        private FeignRequestAdapter(RequestTemplate requestTemplate) {
            this.requestTemplate = requestTemplate;
        }

        @Override
        public Iterator<Map.Entry<String, String>> iterator() {
            throw new UnsupportedOperationException(
"FeignRequestAdapter should only be used with Tracer.inject()");
        }

        @Override
        public void put(String key, String value) {
            requestTemplate.header(key, value);
        }
    }
}


在此配置中:

  • - RequestInterceptor 用於將跟蹤標頭注入傳出 Feign 請求並新增 Micrometer 檢測。
  • - FeignRequestAdapter 類是一個簡單的介面卡,可幫助將跟蹤標頭注入 Feign 請求中。
  • - FeignErrorDecoder 類是一個自定義 Feign 錯誤解碼器,用於記錄失敗請求的指標。

請記住將 Tracer 和 MeterRegistry 替換為應用程式上下文中的適當 bean。此示例假設您使用的是 Spring Boot,但類似的原則也可以應用於其他框架。

相關文章