0%

Feign简介

Feign 进阶特性与实践

Feign 作为 Spring Cloud 生态中重要的服务调用组件,除了基础的声明式接口调用外,还有许多进阶特性可以优化服务间通信的效率、可靠性和可维护性。以下从多个维度展开介绍:

依赖

1
2
3
4
5
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

配置启动类

1
2
3
4
5
6
7
8
9
@SpringBootApplication
@EnableEurekaClient
// 启动feign,会进行对FeignClient的扫描加载
@EnableFeignClients(basePackages = "com.zhanghe.study")
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class,args);
}
}

Feign 配置自定义

Feign 允许通过配置类自定义其核心组件(如编码器、解码器、日志级别等),实现更灵活的调用控制。

1. 全局配置与局部配置

  • 全局配置:通过 @Configuration 注解定义配置类,并在启动类的 @EnableFeignClients 中指定 defaultConfiguration 参数,对所有 Feign 客户端生效。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Configuration
    public class FeignGlobalConfig {
    // 配置日志级别
    @Bean
    public Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL; // 打印所有请求细节
    }

    // 替换默认 HTTP 客户端为 Apache HttpClient(需引入对应依赖)
    @Bean
    public CloseableHttpClient httpClient() {
    return HttpClientBuilder.create().build();
    }
    }

    // 启动类指定全局配置
    @EnableFeignClients(defaultConfiguration = FeignGlobalConfig.class)
  • 局部配置:在 @FeignClientconfiguration 属性中指定配置类,仅对当前客户端生效,优先级高于全局配置。

    1
    2
    3
    4
    5
    6
    7
    @FeignClient(value = "SERVICE-PROVIDER", configuration = FeignLocalConfig.class)
    public interface ProviderClient {

    @RequestMapping(value = "/dept/get/{id}",method = RequestMethod.GET)
    public Dept get(@PathVariable("id") long id);

    }

2. 核心配置项说明

配置项 作用 常用值 / 实现类
Logger.Level 控制日志输出详细程度 NONE(默认)、BASICHEADERSFULL
Encoder/Decoder 自定义请求 / 响应的序列化 / 反序列化 JacksonEncoderGsonDecoder
Contract 定义注解解析规则(默认支持 Spring MVC 注解) SpringMvcContract(默认)、FeignContract(原生注解)
Retryer 配置请求重试策略 Retryer.Default(默认不重试)

3. @FeignClient参数说明

首先主程序入口添加了@EnableFeignClients注解开启对FeignClient扫描加载处理,否则无法加载@FeignClient注解所标注的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public @interface FeignClient {
// 指定了FeignClient的名称,如果使用了Ribbon,该值就是微服务的名称,用于服务发现
@AliasFor("name")
String value() default "";

/** @deprecated */
@Deprecated
String serviceId() default "";
// 指定了FeignClient的名称,如果使用了Ribbon,name就是微服务的名称,用于服务发现,且会作为bean的名称,如果该服务内有多个调用某个微服务的feign接口,可以使用value,而不使用name
@AliasFor("value")
String name() default "";

String qualifier() default "";
// 指定调用的地址,一般不使用该参数,而使用微服务的名称调用
String url() default "";
// 当发生404时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException
boolean decode404() default false;
// 指定Feign的配置类,可以在Feign的配置类中自定义Feign的Encoder、Decoder、LogLevel、Contract
Class<?>[] configuration() default {};
// 失败回调,用于容错
Class<?> fallback() default void.class;
// 工厂生成fallback类实例,可以生成每个接口通用的容错逻辑
Class<?> fallbackFactory() default void.class;
// 定义当前FeignClient的统一前缀
String path() default "";

boolean primary() default true;
}

Feign 与负载均衡、熔断的整合

Feign 本身集成了 Ribbon 负载均衡,且可与熔断器(如 Sentinel、Resilience4j)结合实现服务容错。

1. 负载均衡配置

Feign 默认通过 Ribbon 实现负载均衡,可通过配置文件自定义策略:

1
2
3
4
5
6
# 对 SERVICE-PROVIDER 服务配置负载均衡策略
SERVICE-PROVIDER:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 随机策略
ConnectTimeout: 5000 # 连接超时时间
ReadTimeout: 3000 # 读取超时时间

2. 服务熔断与降级

以 Sentinel 为例(替代停更的 Hystrix):

  • 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    <dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-feign</artifactId>
    </dependency>
  • 配置 fallback 降级类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 定义降级类
    public class ProviderClientFallback implements ProviderClient {
    @Override
    public Dept get(Long id) {
    return new Dept(-1L, "服务降级返回默认数据");
    }
    }

    // Feign 客户端指定降级类
    @FeignClient(
    value = "SERVICE-PROVIDER",
    fallback = ProviderClientFallback.class
    )
    public interface ProviderClient { ... }
  • 开启 Sentinel 支持

    1
    2
    3
    feign:
    sentinel:
    enabled: true # 开启 Feign 与 Sentinel 整合

Feign 性能优化

1. 使用连接池

Feign 默认使用 JDK 原生 URLConnection(无连接池),建议替换为 Apache HttpClient 或 OkHttp 提升性能:

  • 引入 HttpClient 依赖

    1
    2
    3
    4
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    </dependency>
  • 配置连接池参数

    1
    2
    3
    4
    5
    feign:
    httpclient:
    enabled: true
    max-connections: 200 # 最大连接数
    max-connections-per-route: 50 # 每个路由的最大连接数

2. 压缩请求 / 响应

通过开启数据压缩减少网络传输量:

1
2
3
4
5
6
7
8
feign:
compression:
request:
enabled: true
mime-types: application/json,application/xml # 压缩的数据类型
min-request-size: 2048 # 最小压缩阈值(字节)
response:
enabled: true

Feign 常见问题与解决方案

  1. 参数绑定问题
    • 当 Feign 接口方法参数为对象时,需显式添加 @RequestBody 注解(与 Spring MVC 一致)。
    • 路径参数必须用 @PathVariable("name") 指定名称,否则会绑定失败。
  2. 超时设置冲突
    • Feign 的超时时间由 Ribbon 和自身配置共同决定,需注意:Feign 超时时间 > Ribbon 超时时间(避免提前触发重试)。
  3. 日志不输出
    • 除了配置 Logger.Level,还需在日志框架(如 Logback)中开启 Feign 接口所在包的日志级别(如 DEBUG

欢迎关注我的其它发布渠道