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); +} +``` + + +