OpenFeign 是一個宣告式的 Web 服務客戶端,它使得編寫 Web 服務客戶端變得更加容易。OpenFeign 是在 Spring Cloud 生態系統中的一個元件,它整合了 Ribbon(客戶端負載均衡器)和 Eureka(服務發現元件),從而簡化了微服務之間的呼叫。
在 SpringCloud 應用中,我們經常會 使用 OpenFeign,比如透過定義一個介面並使用註解的方式來建立一個 Web 服務客戶端,而不需要編寫大量的模板程式碼。OpenFeign 會自動生成介面的實現類,並使用 Ribbon 來呼叫相應的服務。
我們先來上手用一下,在 Spring Cloud 專案中使用 OpenFeign:
需求:我們的業務場景是這樣的:一個電子商務平臺,其中包含一個商品服務(product-service
)和一個訂單服務(order-service
)。我們要使用 OpenFeign 來實現訂單服務呼叫商品服務的介面。
步驟1:建立商品服務(product-service)
- 新增依賴(
pom.xml
):
<dependencies>
<!-- Spring Boot Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Actuator 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
- 主應用類(
ProductApplication.java
):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProductApplication {
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
}
- 商品控制器(
ProductController.java
):
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
@GetMapping("/products/{id}")
public String getProduct(@PathVariable("id") Long id) {
// 模擬資料庫中獲取商品資訊
return "Product with ID: " + id;
}
}
步驟2:建立訂單服務(order-service)
- 新增依賴(
pom.xml
):
<dependencies>
<!-- Spring Boot Web 依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud OpenFeign 依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
- 主應用類(
OrderApplication.java
):
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
- Feign 客戶端介面(
ProductClient.java
):
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(name = "product-service", url = "http://localhost:8081")
public interface ProductClient {
@GetMapping("/products/{id}")
String getProduct(@PathVariable("id") Long id);
}
- 訂單控制器(
OrderController.java
):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
private final ProductClient productClient;
@Autowired
public OrderController(ProductClient productClient) {
this.productClient = productClient;
}
@GetMapping("/orders/{id}/product")
public String getOrderProduct(@PathVariable("id") Long id) {
// 呼叫商品服務獲取商品資訊
return productClient.getProduct(id);
}
}
步驟3:執行和測試
-
啟動商品服務(
ProductApplication
):- 執行
ProductApplication
的main
方法。
- 執行
-
啟動訂單服務(
OrderApplication
):- 執行
OrderApplication
的main
方法。
- 執行
-
測試呼叫:
- 使用瀏覽器或 Postman 訪問
http://localhost:8082/orders/1/product
,我們就能看到商品服務返回的商品資訊。
- 使用瀏覽器或 Postman 訪問
以上是OpenFeign的基本使用,作為優秀的程式設計師,我們必須要去深入瞭解OpenFeign核心元件背後的實現,知己知彼,方能百戰不殆。下面我們來一起看下 OpenFeign 的一些核心元件及其原始碼分析:
OpenFeign 的核心元件有哪些?
OpenFeign 是 Spring Cloud 生態系統中的一個宣告式 Web 服務客戶端,用於簡化微服務之間的 HTTP 呼叫。
1. Encoder:
在 OpenFeign 中,Encoder
元件負責將請求資料序列化成可以傳送的格式。預設情況下,OpenFeign 只支援將請求資料序列化為字串或位元組陣列。如果需要支援更復雜的物件序列化,可以透過實現自定義的 Encoder
來實現。
我們來分析一下 Encoder
元件的原始碼實現:
步驟1:定義 Encoder 介面
首先,Feign定義了一個 Encoder
介面,該介面包含一個 encode
方法,用於將物件序列化為位元組陣列:
public interface Encoder {
void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
object
:要序列化的物件。bodyType
:物件的型別資訊,通常用於確定如何序列化物件。template
:RequestTemplate
物件,用於設定請求的主體(body)。
步驟2:實現預設 Encoder
OpenFeign 提供了一個預設的 Encoder
實現,通常使用 Jackson 或其他 JSON 庫來序列化物件為 JSON 格式:
public class JacksonEncoder extends SpringEncoder implements Encoder {
public JacksonEncoder(ObjectFactory objectFactory) {
super(objectFactory);
}
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
// 將物件序列化為 JSON 格式,並設定到 RequestTemplate 中
byte[] body = this.objectFactory.createInstance(bodyType).writeValueAsBytes(object);
template.body(body);
template.requestBody(Request.Body.create(body, ContentType.APPLICATION_JSON));
}
}
在這個實現中,JacksonEncoder
使用 Jackson 庫將物件序列化為 JSON,並設定請求的內容型別為 APPLICATION_JSON
。
步驟3:自定義 Encoder 實現
如果我們需要支援其他型別的序列化,可以建立自定義的 Encoder
實現。例如,如果要支援 XML 序列化,可以建立一個使用 JAXB 或其他 XML 庫的 Encoder
:
public class XmlEncoder implements Encoder {
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
// 使用 XML 庫將物件序列化為 XML 格式
String xml = serializeToXml(object);
template.body(xml, ContentType.APPLICATION_XML);
}
private String serializeToXml(Object object) {
// 實現物件到 XML 的序列化邏輯
// ...
return xml;
}
}
步驟4:配置 OpenFeign 客戶端使用自定義 Encoder
在 Feign 客戶端配置中,可以指定自定義的 Encoder
實現:
@Configuration
public class FeignConfig {
@Bean
public Encoder encoder() {
return new XmlEncoder();
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
Encoder
介面,為請求資料的序列化提供了一個擴充套件點。OpenFeign 提供了預設的 JacksonEncoder
實現,它使用 Jackson 庫來序列化物件為 JSON 格式。如果想實現自定義的 Encoder
,來支援其他資料格式,如 XML、Protobuf 等也是非常方便靈活的。
2. Decoder:
OpenFeign 的Decoder
元件負責將HTTP響應體(通常是位元組流)反序列化為Java物件。預設情況下,OpenFeign支援將響應體反序列化為字串或位元組陣列。如果需要支援更復雜的物件反序列化,可以透過實現自定義的 Decoder
來實現。
下面來分析 Decoder
元件的原始碼實現:
步驟1:定義 Decoder 介面
OpenFeign 定義了一個 Decoder
介面,該介面包含一個 decode
方法,用於將響應體反序列化為物件:
public interface Decoder {
<T> T decode(Response response, Type type) throws IOException, DecodeException;
}
T
:要反序列化成的物件型別。response
:Feign的Response
物件,包含了HTTP響應的所有資訊。type
:要反序列化成的物件的型別資訊。
步驟2:實現預設 Decoder
OpenFeign 提供了一個預設的 Decoder
實現,通常使用 Jackson 或其他 JSON 庫來反序列化JSON響應體:
public class JacksonDecoder extends SpringDecoder implements Decoder {
public JacksonDecoder(ObjectFactory objectFactory) {
super(objectFactory);
}
@Override
public <T> T decode(Response response, Type type) throws IOException, DecodeException {
// 從響應中獲取位元組流
InputStream inputStream = response.body().asInputStream();
// 使用 Jackson 庫將位元組流反序列化為 Java 物件
return this.objectFactory.createInstance(type).readValue(inputStream, type);
}
}
在這個實現中,JacksonDecoder
使用 Jackson 庫將響應體的位元組流反序列化為 Java 物件,並根據響應的狀態碼丟擲相應的異常或返回物件。
步驟3:自定義 Decoder 實現
如果咱們需要支援其他型別的反序列化,可以建立自定義的 Decoder
實現。比如要支援 XML 反序列化,可以建立一個使用 JAXB 或其他 XML 庫的 Decoder
:
public class XmlDecoder implements Decoder {
@Override
public <T> T decode(Response response, Type type) throws IOException, DecodeException {
// 從響應中獲取位元組流
InputStream inputStream = response.body().asInputStream();
// 使用 XML 庫將位元組流反序列化為 Java 物件
T object = deserializeFromXml(inputStream, type);
return object;
}
private <T> T deserializeFromXml(InputStream inputStream, Type type) {
// 實現 XML 到 Java 物件的反序列化邏輯
// ...
return null;
}
}
步驟4:配置 OpenFeign 客戶端使用自定義 Decoder
在 Feign 客戶端配置中,可以指定自定義的 Decoder
實現:
@Configuration
public class FeignConfig {
@Bean
public Decoder decoder() {
return new XmlDecoder();
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
OpenFeign 提供了預設的 JacksonDecoder
實現,它使用 Jackson 庫來反序列化 JSON 格式的響應體。咱們還可以透過實現自定義的 Decoder
,可以支援其他資料格式,如 XML等。開發中,咱們可以根據需要選擇或實現適合自己業務場景的反序列化方式。
3. Contract:
OpenFeign的Contract
元件負責將介面的方法和註解轉換為HTTP請求。它定義瞭如何將Java介面對映到HTTP請求上,包括請求的URL、HTTP方法、請求頭、查詢引數和請求體等。Contract
元件是Feign中非常核心的部分,因為它決定了Feign客戶端如何理解和構建HTTP請求。
步驟1:定義 Contract 介面
Feign定義了一個 Contract
介面,該介面包含兩個主要的方法:
public interface Contract {
List<MethodMetadata> parseAndValidatteMethods(FeignTarget<?> target);
RequestTemplate create(Request request, Target<?> target, Method method, Object... argv);
}
parseAndValidatteMethods
:解析並驗證目標介面的方法,生成MethodMetadata
列表,每個MethodMetadata
包含一個方法的所有後設資料。create
:根據Request
和Target
建立RequestTemplate
,用於構建實際的HTTP請求。
步驟2:實現預設 Contract
Feign提供了一個預設的 Contract
實現,通常使用Java的反射API來解析介面的方法和註解:
public class FeignContract implements Contract {
@Override
public List<MethodMetadata> parseAndValidateMethods(FeignTarget<?> target) {
// 解析目標介面的方法,生成 MethodMetadata 列表
// ...
}
@Override
public RequestTemplate create(Request request, Target<?> target, Method method, Object... argv) {
// 根據 MethodMetadata 和引數建立 RequestTemplate
// ...
}
}
在實現中咱們可以看到,FeignContract
會檢查介面方法上的註解(如 @GetMapping
、@PostMapping
等),並根據這些註解構建HTTP請求。
步驟3:自定義 Contract 實現
同樣的道理,如果需要支援自定義的註解或擴充套件Feign的功能,可以透過實現自定義的 Contract
來實現:
public class MyCustomContract implements Contract {
@Override
public List<MethodMetadata> parseAndValidateMethods(FeignTarget<?> target) {
// 自定義解析邏輯
// ...
}
@Override
public RequestTemplate create(Request request, Target<?> target, Method method, Object... argv) {
// 自定義建立 RequestTemplate 的邏輯
// ...
}
}
步驟4:配置 OpenFeign 客戶端使用自定義 Contract
在Feign客戶端配置中,可以指定自定義的 Contract
實現:
@Configuration
public class FeignConfig {
@Bean
public Contract contract() {
return new MyCustomContract();
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
OpenFeign 提供了預設的 FeignContract
實現,它使用Java的反射API來解析介面的方法和註解,並生成 MethodMetadata
。這種方式允許Feign自動處理標準的JAX-RS註解。咱們可以透過實現自定義的 Contract
,可以支援自定義註解或改變Feign的請求構建邏輯。
4. Client:
Client
元件是負責傳送HTTP請求並接收HTTP響應的核心元件。OpenFeign 預設使用Java標準庫中的HttpURLConnection
來傳送請求,但也支援使用其他HTTP客戶端庫,如Apache HttpClient或OkHttp。
步驟1:定義 Client 介面
OpenFeign中定義了一個Client
介面,該介面包含一個execute
方法,用於執行HTTP請求:
public interface Client {
Response execute(Request request, Request.Options options) throws IOException;
}
Request
:代表要執行的HTTP請求。Request.Options
:包含請求的配置選項,如連線超時和讀取超時。Response
:代表HTTP響應。
步驟2:實現預設 Client
OpenFeign 提供了一個預設的Client
實現,使用Java的HttpURLConnection
:
public class HttpURLConnectionClient implements Client {
@Override
public Response execute(Request request, Request.Options options) throws IOException {
// 建立和配置 HttpURLConnection
URL url = new URL(request.url());
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 設定請求方法、頭資訊、超時等
// ...
// 傳送請求並獲取響應
// ...
return response;
}
}
在這個實現中,HttpURLConnectionClient
使用HttpURLConnection
來構建和傳送HTTP請求,並處理響應。
步驟3:自定義 Client 實現
如果我們需要使用其他HTTP客戶端庫,可以建立自定義的Client
實現。例如,使用Apache HttpClient:
public class HttpClientClient implements Client {
private final CloseableHttpClient httpClient;
public HttpClientClient(CloseableHttpClient httpClient) {
this.httpClient = httpClient;
}
@Override
public Response execute(Request request, Request.Options options) throws IOException {
// 使用 Apache HttpClient 構建和傳送HTTP請求
// ...
return response;
}
}
步驟4:配置 Feign 客戶端使用自定義 Client
在Feign配置中,可以指定自定義的Client
實現:
@Configuration
public class FeignConfig {
@Bean
public Client httpClientClient() {
CloseableHttpClient httpClient = HttpClients.createDefault();
return new HttpClientClient(httpClient);
}
}
然後在@FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
OpenFeign 提供了預設的HttpURLConnectionClient
實現,它使用Java標準庫中的HttpURLConnection
來傳送請求。這種方式的好處是簡單且無需額外依賴。也可以透過實現自定義的Client
,如Apache HttpClient或OkHttp。這讓OpenFeign能夠適應不同的效能和功能需求。
5. RequestInterceptor:
RequestInterceptor
是一個非常重要的元件,它允許咱們在請求傳送之前對其進行攔截,從而可以新增一些通用的處理邏輯,比如設定認證頭、日誌記錄、修改請求引數等。我們來分析一下 RequestInterceptor
元件的原始碼實現:
步驟1:定義 RequestInterceptor 介面
Feign定義了一個 RequestInterceptor
介面,該介面包含一個 apply
方法,用於在請求傳送前對 RequestTemplate
進行操作:
public interface RequestInterceptor {
void apply(RequestTemplate template);
}
RequestTemplate
:代表即將傳送的HTTP請求,可以修改URL、頭資訊、請求體等。
步驟2:實現預設 RequestInterceptor
OpenFeign 提供了一些預設的 RequestInterceptor
實現,例如用於設定預設頭資訊的 RequestInterceptor
:
public class DefaultRequestInterceptor implements RequestInterceptor {
private final Configuration configuration;
public DefaultRequestInterceptor(Configuration configuration) {
this.configuration = configuration;
}
@Override
public void apply(RequestTemplate template) {
// 設定預設的頭資訊,比如Content-Type
template.header("Content-Type", configuration.getContentType().toString());
// 可以新增更多的預設處理邏輯
}
}
在這個實現中,DefaultRequestInterceptor
會在每個請求中設定一些預設的頭資訊。
步驟3:自定義 RequestInterceptor 實現
咱們可以根據需要實現自定義的 RequestInterceptor
。例如,新增一個用於設定認證頭的攔截器:
public class AuthRequestInterceptor implements RequestInterceptor {
private final String authToken;
public AuthRequestInterceptor(String authToken) {
this.authToken = authToken;
}
@Override
public void apply(RequestTemplate template) {
// 在每個請求中新增認證頭
template.header("Authorization", "Bearer " + authToken);
}
}
步驟4:配置 OpenFeign 客戶端使用自定義 RequestInterceptor
在 OpenFeign 配置中,可以指定自定義的 RequestInterceptor
實現:
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor authRequestInterceptor() {
// 假設從配置檔案或環境變數中獲取認證令牌
String authToken = "your-auth-token";
return new AuthRequestInterceptor(authToken);
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
RequestInterceptor讓咱們在不修改每個單獨請求的情況下,統一處理請求。這使得Feign客戶端更加靈活和強大,能夠適應各種複雜的業務需求。
6. Retryer:
Retryer
元件負責定義重試策略,它決定了在遇到特定型別的錯誤時是否重試請求,以及重試的次數和間隔。Retryer
是Feign中的一個重要元件,特別是在網路不穩定或服務不穩定的環境中,大派用場,它可以顯著提高系統的健壯性哦。
步驟1:定義 Retryer 介面
Feign定義了一個 Retryer
介面,該介面包含幾個關鍵的方法,用於控制重試的行為:
public interface Retryer {
void continueOrPropagate(RetryableException e);
Retryer clone();
long getDelay(RetryableException e, int attempt);
boolean shouldRetry(RetryableException e, int attempt, int retry);
}
continueOrPropagate
:決定是繼續重試還是丟擲異常。clone
:建立Retryer
的副本,通常用於每個請求的獨立重試策略。getDelay
:返回在下一次重試之前的延遲時間。shouldRetry
:決定是否應該重試請求。
步驟2:實現預設 Retryer
Feign提供了一個預設的 Retryer
實現,通常是一個簡單的重試策略,例如:
public class DefaultRetryer implements Retryer {
private final long period;
private final long maxPeriod;
private final int maxAttempts;
public DefaultRetryer(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
}
@Override
public void continueOrPropagate(RetryableException e) {
// 根據異常型別和重試次數決定是否重試
if (shouldRetry(e, e.getAttempt(), maxAttempts)) {
// 繼續重試
} else {
// 丟擲異常
throw e;
}
}
@Override
public Retryer clone() {
return new DefaultRetryer(period, maxPeriod, maxAttempts);
}
@Override
public long getDelay(RetryableException e, int attempt) {
// 計算重試延遲
return Math.min(period * (long) Math.pow(2, attempt), maxPeriod);
}
@Override
public boolean shouldRetry(RetryableException e, int attempt, int retry) {
// 根據異常型別和重試次數決定是否重試
return attempt < retry;
}
}
在這個實現中,DefaultRetryer
使用指數退避策略來計算重試延遲,並允許指定最大重試次數。
步驟3:自定義 Retryer 實現
當咱們需要更復雜的重試策略時,可以建立自定義的 Retryer
實現。例如,可以基於特定的異常型別或響應碼來決定重試策略:
public class CustomRetryer implements Retryer {
// ... 自定義重試邏輯 ...
@Override
public void continueOrPropagate(RetryableException e) {
// 自定義重試邏輯
}
@Override
public Retryer clone() {
return new CustomRetryer();
}
@Override
public long getDelay(RetryableException e, int attempt) {
// 自定義延遲邏輯
return ...;
}
@Override
public boolean shouldRetry(RetryableException e, int attempt, int retry) {
// 自定義重試條件
return ...;
}
}
步驟4:配置 Feign 客戶端使用自定義 Retryer
在Feign配置中,可以指定自定義的 Retryer
實現:
@Configuration
public class FeignConfig {
@Bean
public Retryer retryer() {
return new CustomRetryer();
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
OpenFeign 允許我們根據需要選擇或實現適合自己業務場景的重試策略,從而提高系統的健壯性和可靠性。問題來了,啥是指數退避策略?
解釋一下哈,指數退避策略(Exponential Backoff)是一種在網路通訊和分散式系統中常用的重試策略,特別是在處理臨時故障或網路延遲時。這種策略旨在透過增加連續重試之間的等待時間來減少系統的負載,並提高重試成功的機會。
指數退避策略的工作原理是這樣的:當發生錯誤或故障時,系統首先會等待一個初始的短暫延遲,然後重試。如果第一次重試失敗,等待時間會指數增長。這意味著每次重試的等待時間都是前一次的兩倍(或另一個指數因子)。
通常會設定一個最大嘗試次數,以防止無限重試。為了避免等待時間過長,會設定一個最大延遲時間,超過這個時間後,即使重試次數沒有達到最大次數,也不會再增加等待時間。
為了減少多個客戶端同時重試時的同步效應,有時會在指數退避中加入隨機化因子,使得每次的等待時間在一定範圍內變化。
7. Configuration:
Configuration
元件是一個關鍵的設定類,它允許使用者自定義Feign客戶端的行為。Configuration
類通常包含了一系列的設定,比如連線超時、讀取超時、重試策略、編碼器、解碼器、契約(Contract)、日誌級別等。這些設定可以應用於所有的Feign客戶端,或者特定的Feign客戶端。
步驟1:定義 Configuration 介面
Feign定義了一個 Configuration
介面,該介面允許使用者配置Feign客戶端的各種引數:
public interface Configuration {
// 返回配置的編碼器
Encoder encoder();
// 返回配置的解碼器
Decoder decoder();
// 返回配置的契約
Contract contract();
// 返回配置的請求攔截器
RequestInterceptor requestInterceptor();
// 返回配置的重試策略
Retryer retryer();
// 返回配置的日誌級別
Logger.Level loggerLevel();
// ... 可能還有其他配置方法 ...
}
步驟2:實現預設 Configuration
Feign提供了一個預設的 Configuration
實現,這個實現包含了Feign的預設行為:
public class DefaultConfiguration implements Configuration {
// ... 定義預設的編碼器、解碼器、契約等 ...
@Override
public Encoder encoder() {
return new JacksonEncoder(...);
}
@Override
public Decoder decoder() {
return new JacksonDecoder(...);
}
@Override
public Contract contract() {
return new SpringMvcContract(...);
}
// ... 實現其他配置方法 ...
}
在這個實現中,DefaultConfiguration
定義了Feign的預設編碼器、解碼器、契約等元件。
步驟3:自定義 Configuration 實現
使用者可以建立自定義的 Configuration
實現,以覆蓋預設的行為:
public class CustomConfiguration extends DefaultConfiguration {
// ... 自定義特定的配置 ...
@Override
public Encoder encoder() {
// 返回自定義的編碼器
return new CustomEncoder(...);
}
@Override
public Decoder decoder() {
// 返回自定義的解碼器
return new CustomDecoder(...);
}
// ... 可以覆蓋其他配置方法 ...
}
步驟4:配置 Feign 客戶端使用自定義 Configuration
在Feign配置中,可以指定自定義的 Configuration
實現:
@Configuration
public class FeignConfig {
@Bean
public Configuration feignConfiguration() {
return new CustomConfiguration(...);
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
8. Target:
Target
元件代表了Feign客戶端將要呼叫的遠端服務的目標。它通常包含了服務的名稱和可能的特定配置,例如請求的URL。Target
元件在Feign的動態代理機制中扮演著重要角色,因為它定義瞭如何將方法呼叫轉換為實際的HTTP請求。
步驟1:定義 Target 介面
Feign定義了一個 Target
介面,該介面包含一些關鍵的方法和屬性:
public interface Target<T> {
String name();
String url();
Class<T> type();
}
name()
:返回服務的名稱,通常用於服務發現。url()
:返回服務的URL,可以是完整的URL或者是一個模板。type()
:返回Feign客戶端介面的型別。
步驟2:實現 Target 介面
Feign提供了 Target
介面的實現,通常是一個名為 HardCodedTarget
的類:
public class HardCodedTarget<T> implements Target<T> {
private final String name;
private final String url;
private final Class<T> type;
public HardCodedTarget(Class<T> type, String name, String url) {
this.type = type;
this.name = name;
this.url = url;
}
@Override
public String name() {
return name;
}
@Override
public String url() {
return url;
}
@Override
public Class<T> type() {
return type;
}
}
在這個實現中,HardCodedTarget
透過建構函式接收服務的名稱、URL和介面型別,並提供相應的getter方法。
步驟3:使用 Target 元件
在Feign客戶端介面中,可以透過 @FeignClient
註解的 value
屬性指定服務名稱,Feign在內部會使用 Target
來構建代理:
@FeignClient(value = "myService", url = "http://localhost:8080")
public interface MyClient extends MyServiceApi {
// 定義服務方法
}
Feign會根據註解資訊建立一個 HardCodedTarget
例項,並使用它來構建動態代理。
步驟4:動態代理和 Target
Feign使用Java的動態代理機制(是不是哪哪都是動態代理,所以說動態代理很重要)來建立客戶端代理。在Feign的 ReflectiveFeign
類中,會使用 InvocationHandlerFactory
來建立 InvocationHandler
:
public class ReflectiveFeign extends Feign {
private final InvocationHandlerFactory invocationHandlerFactory;
public ReflectiveFeign(InvocationHandlerFactory invocationHandlerFactory) {
this.invocationHandlerFactory = invocationHandlerFactory;
}
@Override
public <T> T newInstance(Target<T> target) {
// 使用 InvocationHandlerFactory 建立 InvocationHandler
InvocationHandler invocationHandler = invocationHandlerFactory.create(target);
// 建立動態代理
return (T) Proxy.newProxyInstance(target.type().getClassLoader(),
new Class<?>[]{target.type()}, invocationHandler);
}
}
在這個過程中,InvocationHandler
會使用 Target
來構建實際的HTTP請求。
9. InvocationHandlerFactory:
InvocationHandlerFactory
元件是動態代理的核心,它負責建立 InvocationHandler
,這是Java動態代理機制的關鍵部分。InvocationHandler
定義了代理物件在被呼叫時的行為。在Feign的上下文中,InvocationHandler
負責將方法呼叫轉換為HTTP請求。
步驟1:定義 InvocationHandlerFactory 介面
Feign定義了一個 InvocationHandlerFactory
介面,該介面包含一個方法,用於建立 InvocationHandler
:
public interface InvocationHandlerFactory {
InvocationHandler create(Target target);
}
Target
:代表Feign客戶端的目標,包含了服務的名稱、URL和介面型別。
步驟2:實現 InvocationHandlerFactory
Feign提供了一個預設的 InvocationHandlerFactory
實現,通常是一個名為 ReflectiveInvocationHandlerFactory
的類:
public class ReflectiveInvocationHandlerFactory implements InvocationHandlerFactory {
@Override
public InvocationHandler create(Target target) {
return new ReflectiveInvocationHandler(target);
}
}
在這個實現中,ReflectiveInvocationHandlerFactory
建立了一個 ReflectiveInvocationHandler
例項,這個 InvocationHandler
會處理反射呼叫。
步驟3:實現 InvocationHandler
ReflectiveInvocationHandler
是 InvocationHandler
介面的一個實現,它負責將方法呼叫轉換為HTTP請求:
public class ReflectiveInvocationHandler implements InvocationHandler {
private final Target<?> target;
private final FeignClientFactory feignClientFactory;
public ReflectiveInvocationHandler(Target<?> target, FeignClientFactory feignClientFactory) {
this.target = target;
this.feignClientFactory = feignClientFactory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 根據方法和引數構建RequestTemplate
RequestTemplate template = buildTemplateFromArgs(target, method, args);
// 傳送請求並獲取響應
Response response = feignClientFactory.create(target).execute(template, options());
// 根據響應構建返回值
return decode(response, method.getReturnType());
}
private RequestTemplate buildTemplateFromArgs(Target<?> target, Method method, Object[] args) {
// 構建請求模板邏輯
// ...
}
private Response.Options options() {
// 獲取請求選項,如超時設定
// ...
}
private Object decode(Response response, Type type) {
// 將響應解碼為方法返回型別的邏輯
// ...
}
}
程式碼中我們發現,invoke
方法是核心,它根據目標物件、方法和引數構建一個 RequestTemplate
,然後使用 FeignClient
傳送請求並獲取響應。
步驟4:配置 Feign 客戶端使用自定義 InvocationHandlerFactory
自定義 InvocationHandlerFactory
,可以在Feign配置中指定:
@Configuration
public class FeignConfig {
@Bean
public InvocationHandlerFactory invocationHandlerFactory() {
return new CustomInvocationHandlerFactory();
}
}
然後在 @FeignClient
註解中指定配置類:
@FeignClient(name = "myClient", configuration = FeignConfig.class)
public interface MyClient {
// ...
}
小結一下
為啥說程式設計師需要充分理解設計模式的應用,如果在面試時問你任何關於設計模式的問題,請不要只講概念、不要只講概念、不要只講概念,還要結合業務場景,或者結合框架原始碼的理解來講,講一講解決了什麼問題,這是關鍵所在。
最後
OpenFeign 是 Spring Cloud 生態系統中的一個強大工具,它使得微服務之間的通訊變得更加簡單和高效。透過使用 OpenFeign,開發者可以專注於業務邏輯的實現,而不需要關心底層的 HTTP 通訊細節。歡迎關注威哥愛程式設計,原創不易,求個贊啊兄弟。