# Spring Cloud OpenFeign

万世威 2020-10-16

# 概述

​ OpenFeign 是一个声明式的 web 服务客户端, 让编写 web 服务更加简单, 写一个接口并添加一个注解即可, 具有可插拔注解特性 并同时支持 Feign 注解 和 JAX-RS 注解, Feign 支持可插拔式的编码器和解码器.

​ Spring Cloud 对 Feign 进行了封装, 支持 springmvc 标准注解 和 HttpMessageConverters. Feign 可以与 Ribbon 和 Eureka 组合使用, 以支持 负载均衡

# 使用

1、单独使用

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

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
@FeignClient(url = "http://127.0.0.1:9002/dist", name = "test")
public interface FeignService {

    @GetMapping("/service/{id}")
    String getStore(@PathVariable("id") int id);

    @PostMapping(value = "/file", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String testFileUpload(@RequestBody MultipartFile file);
}
1
2
3
4
5
6
7
8
9
  • url:可在配置文件中定义

2、搭配 Nacos 使用

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

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
spring:
  application:
    name: quick-openfeign
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
1
2
3
4
5
6
7
@FeignClient(value = "quick-provider")
public interface FeignService {

    @GetMapping("/dist/service")
    String getStore();
}
1
2
3
4
5
6
  • "quick-provider", 在 nacos(服务注册中心) 中注册的服务找到真实提供者真实地址信息,如果没有使用 服务注册中心,

这 2 种配置方式,在有 context-path 参数下,需要写明 context-path,可在 @FeignClient 中加入,path 参数值指定

# Feign 默认属性

1、使用 OkHttpClientApacheHttpClien, 同时需要引入相应的包

feign.okhttp.enabled=true
feign.httpclient.enabled=true
1
2

2、配置优先级

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic
1
2
3
4
5
6
7
  • 应用于所有 FeignClient
  • 配置文件优先级更高
  • 如果想 @Configuration 优先级更高,设置 feign.client.default-to-properties=false

3、创建多个具有相同 nameurlfeign client, 使用 contextId 属性配置,避免 bean 名称冲突

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {}

@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {}
1
2
3
4
5

# Feign 降级

@FeignClient(value = "quick-provider", fallback = HystrixClientFallback.class)
public interface FeignService {
    @GetMapping("/dist/service/{id}")
    String getStore(@PathVariable("id") int id);
}

@Component
public class HystrixClientFallback implements FeignService {
    @Override
    public String getStore(int id) { return "hystrix"; }
}
/* 通过 Feign 调用提供者时,提供者方法报错, 执行 HystrixClientFallback 类同名方法 */
1
2
3
4
5
6
7
8
9
10
11
12
  • 拿到降级原因
@FeignClient(value = "quick-provider", fallbackFactory = HystrixClientFallback.class)
public interface FeignService {
    @GetMapping("/dist/service/{id}")
    String getStore(@PathVariable("id") int id);
}


@Component
public class HystrixClientFallback implements FallbackFactory<FeignService> {
    @Override
    public FeignService create(Throwable t) {
        return new FeignService() {
            @Override
            public String getStore(int id) {
                return t.getMessage();
            }
        };
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Feign 继承支持

public interface ProviderService {
    @GetMapping("/dist/service/{id}")
    String getStore(@PathVariable("id") int id);
}

@FeignClient(value = "quick-provider", fallbackFactory = HystrixClientFallback.class)
public interface FeignService extends ProviderService {}
1
2
3
4
5
6
7

# Feign 压缩

feign:
  compression:
    response:
      enabled: true
    request:
      enabled: true
      mime-types: text/xml,application/xml,application/json
      min-request-size: 2048
1
2
3
4
5
6
7
8

# 日志

  • NONE, 不打印 (DEFAULT)
  • BASIC, 只记录请求方法和 URL 以及响应状态代码和执行时间
  • HEADERS, 记录基本信息以及请求和响应标头
  • FULL, 记录请求和响应的头文件,正文和元数据
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FeignBuilderConfiguration {

    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
# application.yaml 中添加以下配置
logging:
  level:
    com.dist.feign: debug # feign 接口所在类包名[可加类名]
1
2
3
4

# 手动创建 Feign 客户端

public interface FeignService {
    @GetMapping("/dist/service/{id}")
    String getStore(@PathVariable("id") int id);
}

@Configuration
@Import(FeignClientsConfiguration.class)
public class FeignConfig {

    @Bean
    public FeignService feignService(Decoder decoder, Encoder encoder, Client client, Contract contract) {
        return Feign.builder().client(client)
                .encoder(encoder)
                .decoder(decoder)
                .contract(contract)
                .target(FeignService.class, "http://quick-provider");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 参考文档

  • https://cloud.spring.io/spring-cloud-openfeign/2.1.x/single/spring-cloud-openfeign.html