服務呼叫 - OpenFeign
??生命不息,寫作不止
?? 繼續踏上學習之路,學之分享筆記
?? 總有一天我也能像各位大佬一樣
?? 一個有夢有戲的人 @怒放吧德德
??分享學習心得,歡迎指正,大家一起學習成長!

介紹
OpenFeign 全稱 Spring Cloud OpenFeign,它是 Spring 官方推出的一種宣告式服務呼叫與負載均衡組件,它的出現就是為了替代進入停更維護狀態的 Feign,
Spring Cloud openfeign對Feign進行了增強,使其支持Spring MVC注解,另外還整合了
Ribbon和Nacos,從而使得Feign的使用更加方便,
Feign使用http遠程呼叫方法就好像呼叫本地的方法,感覺不到是遠程方法,他的使用就和直接寫控制類那樣,暴露介面提供呼叫,我們只需要撰寫呼叫介面+@FeignClient注解,在使用這個api的時候,只需要定義好方法,到時候呼叫這個方法就可以了,這種服務之間的呼叫使用起來是非常的方便,體驗也比較好,
如何實作介面呼叫?
在平時開發的springboot專案中,像這種rest服務是如何被呼叫的呢?通常下是使用Httpclient、Okhttp、HttpURLConnection、RestTemplate,其中RestTemplate是最常見的,之前在 nacos配置中心 使用的是RestTemplate,
SpringCloud整合OpenFeign
就用一個例子來簡單使用OpenFeign進行服務間的呼叫,通過實體來學習關于Feign組件的功能,
引入依賴
使用OpenFeign組件需要引入客戶端依賴
<!--OpenFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
撰寫呼叫介面
通過OpenFeign遠程呼叫服務的時候,比RestTemplate更加方便,就跟撰寫controller介面是差不多的,
需要寫上@FeignClient注解,里面配置微服務名字和rest的@RequestMapping("/api/store"),或者可以在宣告呼叫pai的時候寫上完整的路徑,
簡單的對應如下圖所示

代碼如下:
package com.lyd.demo.feign;
import com.lyd.demo.feign.config.FeignOkhttpConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.Map;
/**
* @author: lyd
* @description: 遠程呼叫 service-store 服務
* @Date: 2022/9/24
* 介紹:
* name / value : 要呼叫的微服務名
* path:控制類上面的路徑 --- @RequestMapping("/api/store")
*/
@FeignClient(name = "service-store", path = "/api/store")
public interface StoreFeignService {
// 宣告要呼叫的rest
@GetMapping("/{id}")
Map<String, Object> getStoreNum(@PathVariable String id);
}
/**
* @RestController
* @RequestMapping("/api/store")
* public class StoreController {
* @Value("${server.port}")
* private String currentPort;
* @GetMapping("/{id}")
* public Map<String, Object> getStoreNum(@PathVariable String id) throws InterruptedException {
* Map<String, Object> map = new HashMap<>();
* map.put("port", currentPort);
* map.put("num", 10);
* return map;
* }
* }
*/
需要在啟動類中寫上注解 @EnableFeignClients
@Autowired
private StoreFeignService storeFeignService;
// 在業務中直接呼叫
storeFeignService.getStoreNum(uid);
OpenFeign自定義配置
Feign 提供了很多的擴展機制,讓用戶可以更加靈活的使用,
feign.Logger.Level:修改日志級別,包含四種不同的級別:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder:回應結果的決議器,http遠程呼叫的結果做決議,例如決議json字串為java物件
feign.codec.Encoder:請求引數編碼,將請求引數編碼,便于通過http請求發送
feign. Contract:支持的注解格式,默認是SpringMVC的注解
feign. Retryer:失敗重試機制,請求失敗的重試機制,默認是沒有,不過會使用Ribbon的重試
日志配置
可以通過配置Feign的日志級別來顯示需要的日志,
1)、定義配置類
定義一個feign的組態檔,并交給spring管理,
feign的日志級別一開始默認是NONE,不顯示任何的日志,可以通過定義一個bean,回傳日志的級別
package com.lyd.demo.feign.config;
import feign.Logger;
import feign.Retryer;
import org.springframework.context.annotation.Bean;
import java.util.concurrent.TimeUnit;
/**
* @author: lyd
* @description: feign組態檔 - 日志
* @Date: 2022/9/24
*/
@Configuration
public class FeignConfig {
@Bean
public Logger.Level feignLoggerLevel() {
return Logger.Level.BASIC;
}
}
日志級別有四種:
- NONE【性能最佳,適用于生產】:不記錄任何日志(默認值),
- BASIC【適用于生產環境追蹤問題】:僅記錄請求方法、URL、回應狀態代碼以及執行時間,
- HEADERS:記錄BASIC級別的基礎上,記錄請求和回應的header,
- FULL【比較適用于開發及測驗環境定位問題】:記錄請求和回應的header、body和元資料,
2)、組態檔設定級別
springboot默認的級別是info,級別比較高,需要在組態檔中配置,如果只在loggin.level下配置級別,就是全域配置,所以我們可以指定包,指定哪個包下面的日志級別,
logging:
level:
com.lyd.demo.feign: debug
3)、配置域
全域配置:
在feign配置類加上@Configuration注解,直接丟給spring來管理,達成全域配置,
區域配置:
①、區域配置可以通過在feign客戶端中指定組態檔,只需要在注解后面加上指定配置類
@FeignClient(name = "service-store", path = "/api/store", configuration = FeignConfig.class)
②、區域配置還可以直接通過yml組態檔來指定,
feign:
client:
config:
service-goods: FULL # 指定哪個服務,并且賦上型別,
配置超時時間
通過yml直接配置超時時間
feign:
client:
config:
default: # 這里用default就是全域配置,如果是寫服務名稱,則是針對某個微服務的配置
connectTimeout: 2000
readTimeout: 2000
在store服務中加個Thread.sleep(5000),就能看到報超時例外SocketTimeoutException,

重試機制配置
通過加入bean來實作
創建重試器 (重試周期(50毫秒),最大重試周期(2000毫秒),最多嘗試次數 3次 ),feign沒有采用線性的重試機制而是采用的是一種指數級(乘法)的重試機制 每次重試時間 當前重試時間*= 1.5
@Bean
public Retryer retryer() {
return new Retryer.Default(50, TimeUnit.SECONDS.toMillis(2), 3);
}
在來看看default的構造器,就能更清楚引數含義,
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}

如圖,會進行重試,直到最后報出例外,
不僅如此,還可以配置契約設定,添加攔截器等等,,,
Feign使用優化
Feign底層發起http請求,依賴于其它的框架,其底層客戶端實作包括:
- URLConnection:默認實作,不支持連接池
- Apache HttpClient :支持連接池
- OKHttp:支持連接池
這次就采用OkHttp
匯入依賴
<!--okHttp-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
設定配置類
package com.lyd.demo.feign.config;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;
/**
* @author: lyd
* @description: OkHttpFeign 的配置
* @Date: 2022/9/24
*/
@Configuration
@ConditionalOnClass({OkHttpClient.class})
@ConditionalOnProperty({"feign.okhttp.enabled"})
public class FeignOkhttpConfig {
@Bean
public okhttp3.OkHttpClient okHttpClient(OkhttpProperties okhttpProperties) {
return new okhttp3.OkHttpClient.Builder()
//設定連接超時
.connectTimeout(okhttpProperties.getConnectTimeout(), TimeUnit.MILLISECONDS)
//設定讀超時
.readTimeout(okhttpProperties.getReadTimeout(), TimeUnit.MILLISECONDS)
//是否自動重連
.retryOnConnectionFailure(true)
.connectionPool(new ConnectionPool())
.addInterceptor(new OkHttpLogInterceptor())
//構建OkHttpClient物件
.build();
}
}
yml配置
feign:
client:
config:
default:
connectTimeout: 2000
readTimeout: 2000
httpclient:
enabled: false
okhttp:
enabled: true
connectTimeout: 4000
readTimeout: 3000
通過類獲取超時時間
package com.lyd.demo.feign.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author: lyd
* @description: 配置引數
* @Date: 2022/9/24
*/
@Data
@Component
@ConfigurationProperties(prefix = "feign.okhttp")
public class OkhttpProperties {
private Long connectTimeout;
private Long readTimeout;
}
攔截器
可以在攔截器中配置業務需求的代碼,
package com.lyd.demo.feign.config;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import java.io.IOException;
/**
* @author: lyd
* @description: 攔截器
* @Date: 2022/9/24
*/
@Slf4j
public class OkHttpLogInterceptor implements Interceptor {
@Override
public Response intercept(Interceptor.Chain chain) throws IOException {
//這個chain里面包含了request和response,所以你要什么都可以從這里拿
Request request = chain.request();
long t1 = System.nanoTime();//請求發起的時間
log.info(String.format("發送請求 %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();//收到回應的時間
//注意這里不能直接使用response.body().string()的方式輸出日志
//因為response.body().string()之后,response中的流會被關閉,程式會報錯,我們需要創建出一個新的response給應用層處理
ResponseBody responseBody = response.peekBody(1024 * 1024);
log.info(String.format("接收回應: [%s] %n回傳json:【%s】 %.1fms%n%s",
response.request().url(),
responseBody.string(),
(t2 - t1) / 1e6d,
response.headers()));
return response;
}
}
引入配置
@FeignClient(name = "service-store", path = "/api/store", configuration = FeignOkhttpConfig.class)
運行結果:

??創作不易,可能有些語言不是很通暢,如有錯誤請指正,感謝觀看!記得點贊哦!??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/509406.html
標籤:Java
上一篇:springcloud快速入門
