全網(wǎng)最詳細(xì)的OpenFeign講解,肯定有你不知道的
環(huán)境:SpringCloud3.1.5
1. 簡介
Spring Cloud OpenFeign是一種聲明式、模板化的HTTP客戶端,它簡化了遠(yuǎn)程調(diào)用的編程體驗(yàn)。在Spring Cloud中使用OpenFeign,開發(fā)者可以像調(diào)用本地方法一樣使用HTTP請求訪問遠(yuǎn)程服務(wù),而無需感知這是在調(diào)用遠(yuǎn)程方法。
OpenFeign會根據(jù)帶有注解的函數(shù)信息構(gòu)建出網(wǎng)絡(luò)請求的模板,在發(fā)送網(wǎng)絡(luò)請求之前,OpenFeign會將函數(shù)的參數(shù)值設(shè)置到這些請求模板中。
在項(xiàng)目啟動階段,OpenFeign框架會自動的掃包流程,從指定的目錄下掃描并加載所有被@FeignClient注解修飾的接口。OpenFeign會針對每一個FeignClient接口生成一個動態(tài)代理(JDK)對象,這個動態(tài)代理對象會被添加到Spring上下文中,并注入到對應(yīng)的服務(wù)里。
總的來說,Spring Cloud OpenFeign是一種強(qiáng)大的工具,它可以幫助開發(fā)者更輕松地實(shí)現(xiàn)微服務(wù)的遠(yuǎn)程調(diào)用。
2. OpenFeign配置
2.1 開啟功能
引入依賴
groupId: org.springframework.cloud
artifactId: spring-cloud-starter-openfeign
開啟
@SpringBootApplication
@EnableFeignClients
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
示例
@FeignClient(name = "demoService")
public interface DemoFeign {
@GetMapping("/info/{id}")
public Object info(@PathVariable("id") Integer id) ;
}
在 @FeignClient 注解中,name="demoService"是一個客戶端名稱,用于創(chuàng)建 Spring Cloud LoadBalancer 客戶端。你也可以設(shè)置 url 屬性(絕對值或主機(jī)名)指定一個 URL。
2.2 自定義配置
通過指定@FeignClient configuration屬性實(shí)現(xiàn)自定義配置
@FeignClient(name = "demoService", configuration=DemoConfiguration.class)
public interface DemoFeign {
}
默認(rèn)情況下Spring Cloud OpenFeign會提供如下的默認(rèn)Bean:
Decoder feignDecoder:ResponseEntityDecoder 響應(yīng)內(nèi)容解碼
Encoder feignEncoder: SpringEncoder
Logger feignLogger: Slf4jLogger
MicrometerCapability micrometerCapability: 如果 feign-micrometer 位于類路徑上,且 MeterRegistry 可用
CachingCapability cachingCapability: 如果使用 @EnableCaching 注解??赏ㄟ^ feign.cache.enabled 關(guān)閉。
Contract feignContract: SpringMvcContract這個的作用就是用來接下@FeignClient注解類中的相應(yīng)MVC注解
Feign.Builder feignBuilder: FeignCircuitBreaker.Builder構(gòu)建Feign客戶端的,比如環(huán)境中使用了CircuitBreaker,那么就會在調(diào)用接口時(shí)應(yīng)用斷路器功能
Client feignClient: 如果 Spring Cloud LoadBalancer 位于類路徑上,則使用 FeignBlockingLoadBalancerClient。如果它們都不在類路徑上,則使用默認(rèn)的 Feign Client。默認(rèn)的Client使用jdk自定的網(wǎng)絡(luò)請求 URLConnection。
我們可以在自定義的DemoConfiguration中自定義自己的實(shí)現(xiàn),在DemoConfiguration中你可以自定義如下的Bean:注意:這些bean是 OpenFeign沒有提供的,除了上面介紹的你可以覆蓋自定義實(shí)現(xiàn),你還可以定義下面的bean
Logger.Level
Retryer
ErrorDecoder
Request.Options
Collection<RequestInterceptor>
SetterFactory
QueryMapEncoder
Capability (MicrometerCapability and CachingCapability are provided by default)
接下來會介紹常用的一些自定義配置
- 動態(tài)配置name & url
@FeignClient(name = "${pack.demo.name}", url="${pack.demo.url}")
public interface DemoFeign {
}
配置文件
pack:
demo:
name: demoService
url: http://localhost:8088/demos
- 超時(shí)配置
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
默認(rèn)所有的接口請求超時(shí)都是5s。
- 動態(tài)設(shè)置超時(shí)時(shí)間
如果啟用了 Feign 客戶端刷新,則每個 Feign 客戶端都會以 feign.Request.Options 作為 refresh-scoped Bean 來創(chuàng)建。這意味著 connectTimeout 和 readTimeout 等屬性可以通過 POST /actuator/refresh 針對任何 Feign 客戶端實(shí)例進(jìn)行刷新。值需要進(jìn)行如下配置接口
feign:
client:
refresh-enabled: true
- 自定義日志級別
配置文件中設(shè)置
logging:
level:
'[com.pack.feign.test.DemoFeign]': debug
配置日志基本,兩種方式:1 配置文件;2 定義Bean
feign:
client:
config:
demoService:
logger-level: full
在自定義配置類中定義為bean
@Bean
public Logger.Level loggerLevel() {
return Logger.Level.FULL ;
}
- 自定義重試機(jī)制
默認(rèn)情況下,當(dāng)請求的接口出現(xiàn)異常不會進(jìn)行重試,可以通過定義下面bean進(jìn)行重試
@Bean
public Retryer feignRetryer() {
Retryer.Default retryer = new Retryer.Default(100, SECONDS.toMillis(1), 2);
return retryer ;
}
- 自定義攔截器
默認(rèn)OpenFeign不提供任何攔截器,如果你需要在請求前進(jìn)行處理,比如添加請求header,那么你可以自定義攔截器實(shí)現(xiàn)
@Bean
public RequestInterceptor headerRequestInterceptor() {
return template -> {
template.header("X-API-TOKEN", "666666") ;
} ;
}
也可以通過配置文件配置
feign:
client:
config:
demoService:
request-interceptors:
- com.pack.feign.HeaderRequestInterceptor
- 手動創(chuàng)建Feign客戶端
在某些情況下,可能需要自定義 Feign 客戶端,你可以使用 Feign Builder API 創(chuàng)建客戶端。下面的示例創(chuàng)建了兩個具有相同接口的 Feign Client,但每個客戶端都配置了單獨(dú)的請求攔截器。
public class DemoController {
private DemoFeign demoFeign ;
@Autowired
public DemoController(Client client, Encoder encoder, Decoder decoder) {
this.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.requestInterceptor(new HeaderRequestInterceptor("X-API-TOKEN", "666666"))
.target(FooClient.class, "http://localhost:8088/demos");
}
}
- 服務(wù)降級
當(dāng)你的類路徑下有了Spring Cloud CircuitBreaker后服務(wù)降級fallback才會生效。
@FeignClient(
url = "http://localhost:8088/demos",
name = "demoService",
configuration = DemoFeignConfiguration.class,
fallback = DemoFeignFallback.class
)
public interface DemoFeign {
@GetMapping("/info/{id}")
public Object info(@PathVariable("id") Integer id) ;
}
服務(wù)降級類必須實(shí)現(xiàn)對應(yīng)FeignClient的接口
public class DemoFeignFallback implements DemoFeign {
public Object info(Integer id) {
return "default - " + id;
}
}
- 服務(wù)降級工廠
如果需要訪問觸發(fā)回退的原因,可以使用 @FeignClient 內(nèi)的 fallbackFactory 屬性。
@FeignClient(
url = "http://localhost:8088/demos",
name = "demoService",
configuration = DemoFeignConfiguration.class,
fallbackFactory = DemoFeignFallbackFactory.class,
)
public interface DemoFeign {
@GetMapping("/info/{id}")
public Object info(@PathVariable("id") Integer id) ;
}
工廠類
public class DemoFeignFallbackFactory implements FallbackFactory<DemoFeignFallback> {
static class DemoFeignFallback implements DemoFeign {
private Throwable cause ;
public DemoFeignFallback(Throwable cause) {
this.cause = cause ;
}
@Override
public Object info(Integer id) {
return "接口調(diào)用異常 - " + this.cause.getMessage() ;
}
}
@Override
public DemoFeignFallback create(Throwable cause) {
return new DemoFeignFallback(cause) ;
}
}
- 緩存支持
如果使用 @EnableCaching 注解,則會創(chuàng)建并注冊一個 CachingCapability Bean,以便 Feign 客戶端識別其接口上的 @Cache* 相關(guān)注解:
@FeignClient(
url = "http://localhost:8088/demos",
name = "demoService",
configuration = DemoFeignConfiguration.class,
fallbackFactory = DemoFeignFallbackFactory.class,
)
public interface DemoFeign {
@GetMapping("/info/{id}")
@Cacheable(cacheNames = "demo-cache", key = "#id")
public Object info(@PathVariable("id") Integer id) ;
}
- Feign繼承支持
Feign 通過單繼承接口支持模板應(yīng)用程序。這樣就可以將常用操作歸類到方便的基礎(chǔ)接口中。
@FeignClient(
url = "http://localhost:8088/users",
name = "userService"
)
public interface UserService {
@GetMapping("/{id}")
User getUser(@PathVariable("id") Long id) ;
}
@FeignClient("users")
public interface UserClient extends UserService {
}
注意:@FeignClient 接口不應(yīng)在服務(wù)器和客戶端之間共享,并且不再支持在類級使用 @RequestMapping 對 @FeignClient 接口進(jìn)行注解。
- 請求相應(yīng)壓縮
你可以考慮為你的 Feign 請求啟用請求或響應(yīng) GZIP 壓縮。你可以啟用其中一個屬性:
feign:
compression:
request
enabled: true
response:
enabled: true
控制壓縮MeidaType類型和最小請求閾值長度
feign:
compression:
request:
mime-types: text/xml,application/xml,application/json
min-request-size: 2048
3. 關(guān)于響應(yīng)式客戶端
由于 OpenFeign 項(xiàng)目目前不支持 Spring WebClient 等反應(yīng)式客戶端,因此 Spring Cloud OpenFeign 也不支持反應(yīng)式客戶端。
目前官方推薦的響應(yīng)式庫:feign-reactive
<dependency>
<groupId>com.playtika.reactivefeign</groupId>
<artifactId>feign-reactor-webclient</artifactId>
<version>3.3.0</version>
</dependency>
我目前使用的是3.3.0版本,除了注解適當(dāng)調(diào)整,其它使用基本一致,非常爽。
@ReactiveFeignClient(
url = "http://localhost:8088/demos",
name = "demoReactorFeign",
fallback = DemoReactorFeignFallback.class,
configuration = {DemoReactorFeignConfig.class}
)
public interface DemoReactorFeign {
@GetMapping("/info/{id}")
public Mono<Object> info(@PathVariable("id") Integer id) ;
}
總結(jié):
OpenFeign是一個非常有用的工具,它為開發(fā)者提供了一種簡單而強(qiáng)大的方式來處理遠(yuǎn)程服務(wù)調(diào)用。通過使用OpenFeign,開發(fā)者可以專注于業(yè)務(wù)邏輯,而無需花費(fèi)太多精力在復(fù)雜的網(wǎng)絡(luò)編程細(xì)節(jié)上。
OpenFeign的聲明式編程風(fēng)格使得我們能夠以一種高度抽象和簡潔的方式與遠(yuǎn)程服務(wù)進(jìn)行交互,它簡化了網(wǎng)絡(luò)請求的構(gòu)建和發(fā)送過程,降低了開發(fā)的復(fù)雜度。
總的來說,Spring Cloud OpenFeign是一個強(qiáng)大而靈活的工具,它可以幫助開發(fā)者更高效地構(gòu)建和維護(hù)微服務(wù)應(yīng)用。通過使用OpenFeign,我們可以更好地專注于業(yè)務(wù)邏輯,而無需擔(dān)心網(wǎng)絡(luò)調(diào)用的實(shí)現(xiàn)細(xì)節(jié)。
完畢?。?!