From 63725457cb155ede6deb43f5a80599123c63e4b0 Mon Sep 17 00:00:00 2001
From: Rikako Wu <496063163@qq.com>
Date: Fri, 14 Apr 2023 15:29:10 +0800
Subject: [PATCH] =?UTF-8?q?Spring=20Cloud=20OpenFeign=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=E9=98=85=E8=AF=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
spring/Spring Cloud/Spring Cloud OpenFeign.md | 161 ++++++++++++++++++
1 file changed, 161 insertions(+)
create mode 100644 spring/Spring Cloud/Spring Cloud OpenFeign.md
diff --git a/spring/Spring Cloud/Spring Cloud OpenFeign.md b/spring/Spring Cloud/Spring Cloud OpenFeign.md
new file mode 100644
index 0000000..f8fdd6f
--- /dev/null
+++ b/spring/Spring Cloud/Spring Cloud OpenFeign.md
@@ -0,0 +1,161 @@
+- [Spring Cloud OpenFeign](#spring-cloud-openfeign)
+ - [Feign](#feign)
+ - [引入Feign](#引入feign)
+ - [@FeignClient注解的属性解析模式](#feignclient注解的属性解析模式)
+ - [覆盖Feign的默认属性](#覆盖feign的默认属性)
+ - [重试](#重试)
+ - [通过Configuration类或配置文件来修改Feign Client的默认行为](#通过configuration类或配置文件来修改feign-client的默认行为)
+ - [超时处理](#超时处理)
+ - [Feign Caching](#feign-caching)
+
+
+# Spring Cloud OpenFeign
+## Feign
+Feign是一个声明式的web service client,其能够让编写web service client的过程更加简单。在使用Feign时,只需要创建一个接口并且为其添加注解。
+在使用Feign时,Spring Cloud集成了Eureka、Spring Cloud CircuitBreaker和Spring Cloud LoadBalancer来向使用者提供一个负载均衡的http client。
+### 引入Feign
+如果要在项目中引入Feign,可以添加如下启动器依赖:
+```xml
+org.springframework.cloud
+spring-cloud-starter-openfeign
+```
+在使用Feign时,需要为启动类添加`@EnableFeignClients`注解,示例如下所示:
+```java
+@SpringBootApplication
+@EnableFeignClients
+public class Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
+```
+使用Feign时,定义接口并添加注解的示例如下所示:
+```java
+@FeignClient("stores")
+public interface StoreClient {
+ @RequestMapping(method = RequestMethod.GET, value = "/stores")
+ List getStores();
+
+ @RequestMapping(method = RequestMethod.GET, value = "/stores")
+ Page getStores(Pageable pageable);
+
+ @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
+ Store update(@PathVariable("storeId") Long storeId, Store store);
+
+ @RequestMapping(method = RequestMethod.DELETE, value = "/stores/{storeId:\\d+}")
+ void delete(@PathVariable Long storeId);
+}
+```
+上述示例中,@FeignClient中指定的字符串是一个任意的client name,用于创建spring cloud loadbalancer client,对于该注解还可以指定一个`url`属性。在spring context中,该bean对象的name即是接口的全类名,bean name也可以通过@FeignClient注解中的`qualifiers`属性来替换。
+上述的loadbalancer client会取发现"stores" service对应的物理地址,如果你的应用是一个Eureka client,那么loadbalancer client会从Eureka注册中心中进行解析。
+#### @FeignClient注解的属性解析模式
+当创建Feign client bean对象时,会解析传递给@FeignClient注解的属性值。这些值默认是立即解析的。
+如果如果需要延迟这些属性值的解析时机,可以配置`spring.cloud.openfeign.lazy-attributes-resolution`的值为true。
+### 覆盖Feign的默认属性
+Spring Cloud Feign的核心理念是命名客户端(named client)。每个Feign client都是组件集合一部分,组件集合协同工作,并且在需要时连接远程的server。该组件集合有一个名称,该值通过@FeignClient的value属性赋值。对于每个named client,Spring Cloud使用`FeignClientsConfiguration`来创建一个新的集合作为Application Client。
+组件集合中包含有一个`feign.Decoder`,一个`feign.Encoder`和`feign.Contract`。可以通过@FeignClient注解中的`contextId`属性来覆盖集合name。
+Spring Cloud允许通过声明额外的configuration来完全掌控feign client,示例如下:
+```java
+@FeignClient(name = "stores", configuration = FooConfiguration.class)
+public interface StoreClient {
+ //..
+}
+```
+在这种情况下,client由`FeignClientsConfiguration`中已经存在的组件和`FooConfiguration`中的组件共同组成,并且后者会覆盖前者。
+> FooConfiguration类在声明时不要为其添加@Configuration注解,否则其会成为feign.Decoder, feign.Encoder, feign.Contract的默认来源。
+
+对于`name`和`url`属性,赋值支持占位符:
+```java
+@FeignClient(name = "${feign.name}", url = "${feign.url}")
+public interface StoreClient {
+ //..
+}
+```
+#### 重试
+一个`Retryer.NEVER_RETRY`的bean对象会被默认创建,其会禁止重试操作。但是,其会自动重试IOException,将IOException看作暂时性地网络异常,并且在ErrorDecoder中抛出RetryableException。
+在@FeignClient注解`configuration`属性指定的配置类中创建Bean对象可以允许覆盖默认的bean对象,示例如下:
+```java
+@Configuration
+public class FooConfiguration {
+ @Bean
+ public Contract feignContract() {
+ return new feign.Contract.Default();
+ }
+
+ @Bean
+ public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
+ return new BasicAuthRequestInterceptor("user", "password");
+ }
+}
+```
+#### 通过Configuration类或配置文件来修改Feign Client的默认行为
+@FeignClient也可以通过配置文件来进行配置:
+```yml
+spring:
+ cloud:
+ openfeign:
+ client:
+ config:
+ {feignName}:
+ url: http://remote-service.com
+ connectTimeout: 5000
+ readTimeout: 5000
+ loggerLevel: full
+ errorDecoder: com.example.SimpleErrorDecoder
+ retryer: com.example.SimpleRetryer
+ defaultQueryParameters:
+ query: queryValue
+ defaultRequestHeaders:
+ header: headerValue
+ requestInterceptors:
+ - com.example.FooRequestInterceptor
+ - com.example.BarRequestInterceptor
+ responseInterceptor: com.example.BazResponseInterceptor
+ dismiss404: false
+ encoder: com.example.SimpleEncoder
+ decoder: com.example.SimpleDecoder
+ contract: com.example.SimpleContract
+ capabilities:
+ - com.example.FooCapability
+ - com.example.BarCapability
+ queryMapEncoder: com.example.SimpleQueryMapEncoder
+ micrometer.enabled: false
+```
+feignName在上述配置文件中代表@FeignClient注解中的value值。
+> 若feignName的值为default,可以将配置应用于所有的feign client。
+
+可以在`@EnableFeignClients`注解中指定defaultConfiguration属性,其可以指定一个自定义的默认配置类,覆盖的行为会作用于所有的Feign Client。
+可以使用`spring.cloud.openfeign.client.config.feignName.defaultQueryParameters`和`spring.cloud.openfeign.client.config.feignName.defaultRequestHeaders`来指定client每次请求都会发送的请求参数和请求header。
+
+如果在创建Configuration类的同时也指定了配置文件,那么配置文件的优先级更高,配置文件的值会覆盖Configuration类中的值。
+如果想要多个feign client拥有相同的name和url,以使它们指向同一个server,但不同feign client拥有不同的配置。可以为它们指定不同的`contextId`。
+```java
+@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
+public interface FooClient {
+ //..
+}
+@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
+public interface BarClient {
+ //..
+}
+```
+### 超时处理
+可以为named client或所有client指定超时处理。OpenFeign使用两个timeout参数:
+- connectTimeout:该属性避免server长时间处理造成的调用方阻塞
+- readTimeout:该属性从连接建立时开始应用,并且当返回响应过慢时被触发(该参数衡量的是从建立连接到返回响应的过程)
+
+### Feign Caching
+当使用@EnableCaching之后,一个`CachingCapability`的bean对象将会被配置,Feign client将会识别接口上的`@Cache*`注解。
+```java
+public interface DemoClient {
+
+ @GetMapping("/demo/{filterParam}")
+ @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
+ String demoEndpoint(String keyParam, @PathVariable String filterParam);
+}
+```
+
+
+