Files
rikako-note/spring/Spring Cloud/Spring Cloud OpenFeign.md

8.3 KiB
Raw Blame History

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可以添加如下启动器依赖

<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>

在使用Feign时需要为启动类添加@EnableFeignClients注解,示例如下所示:

@SpringBootApplication
@EnableFeignClients
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

使用Feign时定义接口并添加注解的示例如下所示

@FeignClient("stores")
public interface StoreClient {
    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    List<Store> getStores();

    @RequestMapping(method = RequestMethod.GET, value = "/stores")
    Page<Store> 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 clientSpring Cloud使用FeignClientsConfiguration来创建一个新的集合作为Application Client。
组件集合中包含有一个feign.Decoder,一个feign.Encoderfeign.Contract。可以通过@FeignClient注解中的contextId属性来覆盖集合name。
Spring Cloud允许通过声明额外的configuration来完全掌控feign client示例如下

@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}

在这种情况下client由FeignClientsConfiguration中已经存在的组件和FooConfiguration中的组件共同组成,并且后者会覆盖前者。

FooConfiguration类在声明时不要为其添加@Configuration注解否则其会成为feign.Decoder, feign.Encoder, feign.Contract的默认来源。

对于nameurl属性,赋值支持占位符:

@FeignClient(name = "${feign.name}", url = "${feign.url}")
public interface StoreClient {
    //..
}

重试

一个Retryer.NEVER_RETRY的bean对象会被默认创建其会禁止重试操作。但是其会自动重试IOException将IOException看作暂时性地网络异常并且在ErrorDecoder中抛出RetryableException。
在@FeignClient注解configuration属性指定的配置类中创建Bean对象可以允许覆盖默认的bean对象示例如下

@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也可以通过配置文件来进行配置

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.defaultQueryParametersspring.cloud.openfeign.client.config.feignName.defaultRequestHeaders来指定client每次请求都会发送的请求参数和请求header。

如果在创建Configuration类的同时也指定了配置文件那么配置文件的优先级更高配置文件的值会覆盖Configuration类中的值。
如果想要多个feign client拥有相同的name和url以使它们指向同一个server但不同feign client拥有不同的配置。可以为它们指定不同的contextId

@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*注解。

public interface DemoClient {

    @GetMapping("/demo/{filterParam}")
    @Cacheable(cacheNames = "demo-cache", key = "#keyParam")
    String demoEndpoint(String keyParam, @PathVariable String filterParam);
}