Spring Cloud 声明式REST客户端 OpenFeign -- 2. 覆盖Feign缺省值

本文深入探讨SpringCloud Feign客户端的核心概念,包括命名客户端、组件配置及自定义选项。介绍了如何通过注解和配置文件定制Feign客户端,实现高级控制。

关于Spring Cloud Feign,一个核心概念是命名客户端(named client)。每个feign client可以被理解成是一整套组件的一部分,这套组件一块工作,按需跟远程服务器发生联系,这整套组件有一个名字,就是应用开发人员通过@FeignClient所指定的名字。Spring Cloud根据FeignClientsConfiguration配置,针对每一个命名的feign客户端,将它的这整套组件创建为一个ApplicationContext实例存在。这里面包含了一个feign.Decoder,一个feign.Encoder和一个feign.Contract。并且使用注解@FeignClient的属性contextId可以覆盖这整套组件的名称。

通过使用@FeignClient注解属性configuration增加额外的配置,Spring Cloud允许你完全控制feign客户端。例子如下 :


// 这里 FooConfiguration 会叠加在缺省 FeignClientsConfiguration 之上
// 对 feign client stores 生效
@FeignClient(name = "stores", configuration = FooConfiguration.class)
public interface StoreClient {
    //..
}

该例子中的feign客户端会结合考虑FeignClientsConfiguration配置和自定义配置FooConfiguration最终生成,并且FooConfiguration配置中的定义优先级高。

注意 : FooConfiguration不必要使用注解@Configuration。不过如果FooConfiguration使用了注解@Configuration,则注意把它从@ComponentScan中排除掉,要不然该配置会被@ComponentScan包含进来,这样它就会变成feign.Decoder,feign.Encoder,feign.Contract等组件的缺省来源。想避免FooConfiguration@ComponentScan看到,可以将它放到一个独立的,跟任何@ComponentScan/@SpringBootApplication可见包都不重叠的包中。或者将它明确地从@ComponentScan注解中排除。

注意 : serviceId属性现在已经不建议使用了,推荐使用name

注意 : @FeignClient注解属性contextId可以用于修改整个套组ApplicationContext的名字,它会覆盖客户端名称的别名,也会被用作该feign客户端配置bean名称的一部分。

注意 : 以前使用url属性,不需要name属性。现在必须要使用name属性。

@FeignClientname/url属性值可以使用占位符,如下所示:


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

Spring Cloud Netflixfeign客户端缺省提供以下bean组件:

BeanTypebeanNameClassName
DecoderfeignDecoderResponseEntityDecoder (包了一个SpringDecoder)
EncoderfeignEncoderSpringEncoder
LoggerfeignLoggerSlf4jLogger
ContractfeignContractSpringMvcContract
Feign.BuilderfeignBuilderHystrixFeign.Builder
ClientfeignClient如果启用了Ribbon会是LoadBalancerFeignClient,否则会使用缺省的client

OkHttpClient或者ApacheHttpClient 放在classpath中,并且相应设置feign.okhttp.enabled 或者feign.httpclient.enabledtrue, 那就会使用相应的这些HTTP客户端。想定制HTTP客户端的话,使用ApacheHttpClient时,你可以提供一个类型为ClosableHttpClientbean;使用OK HTTP的话,提供一个类型为OkHttpClientbean

缺省情况下,Spring Cloud Netflix没有给feign提供以下bean组件,但是它仍然会从应用上下文中查找这些bean组件用于创建feign客户端 (这里其实给开发人员提供了定制feign客户端的机会):

  • Logger.Level
  • Retryer
  • ErrorDecoder
  • Request.Options
  • Collection<RequestInterceptor>
  • SetterFactory

@FeignClient配置(比如上面提到的配置FooConfiguration)中定义这里描述的任何一种类型的bean,你就可以覆盖框架缺省提供的这种类型的bean。比如 :

@Configuration
public class FooConfiguration {
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }
}

通过该例子,开发人员指定使用feign.Contract.Default而不再使用Spring Cloud缺省的SpringMvcContract。并且开发人员往RequestInterceptor集合中增加了一个自定义的RequestInterceptor:BasicAuthRequestInterceptor

@FeignClient也可以通过配置文件配置 , 如下例子所示 :

application.yml配置文件内容 :

feign:
  client:
    config:
      feignName:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        retryer: com.example.SimpleRetryer
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        decode404: false
        encoder: com.example.SimpleEncoder
        decoder: com.example.SimpleDecoder
        contract: com.example.SimpleContract

通过注解@EnableFeignClients的属性defaultConfiguration可以为所有feign客户端提供缺省配置,配置方式跟上面提到的方式类似。区别是缺省属性是面向所有feign客户端的。

如果你喜欢使用配置文件配置所有@FeignClient,你可以使用缺省配置属性,也就是feign客户端名称为default,例子如下所示 :

application.yml配置文件内容 :

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

如果你既定义了@Configuration配置类bean又使用了配置属性,配置属性会被优先使用,它会覆盖@Configuration配置类bean中的值。但是如果你想指定使用@Configuration,可以设置feign.client.default-to-propertiesfalse

如果你想在RequestInterceptor中使用ThreadLocal绑定变量,你需要将Hystrix的线程隔离策略设置为SEMAPHORE或者在Feign中禁用Hystrix

application.yml`配置文件内容 :


# 禁用Feign中的Hystrix:
feign:
  hystrix:
    enabled: false

# 设置线程隔离级别为SEMAPHORE:
hystrix:
  command:
    default:
      execution:
        isolation:
          strategy: SEMAPHORE

如果你想创建多个同名或者url相同的feign客户端以确保它们指向同一个服务器但是又要它们使用不同的配置,那么我们必须使用不同的contextId以避免这些配置bean的名字冲突。例子如下所示 :

@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
public interface FooClient {
    //..
}
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
public interface BarClient {
    //..
}
Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer? at org.springframework.cloud.openfeign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:382) ~[spring-cloud-openfeign-core-3.1.9.jar:3.1.9] at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:427) ~[spring-cloud-openfeign-core-3.1.9.jar:3.1.9] at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:402) ~[spring-cloud-openfeign-core-3.1.9.jar:3.1.9] at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235) ~[spring-cloud-openfeign-core-3.1.9.jar:3.1.9] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[spring-beans-5.3.31.jar:5.3.31] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[spring-beans-5.3.31.jar:5.3.31] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.31.jar:5.3.31] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.31.jar:5.3.31] ... 42 common frames omitted 进程已结束,退出代码为 1
最新发布
09-10
你遇到的错误是: ``` Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer? ``` --- ## 🔍 错误原因分析 这个错误说明: > Spring Cloud Feign 在尝试创建 Feign Client 时,发现没有可用的 **LoadBalancer** 实现,也就是说没有引入支持负载均衡的依赖包。 从 Spring Cloud 2020.0(即 Ilford 版本)开始,Netflix Ribbon 被弃用,**Spring Cloud LoadBalancer** 成为了默认的客户端负载均衡器。 如果你使用的是 Spring Cloud 2020.x 或更新的版本(如 2021.x、2022.x),而没有引入 `spring-cloud-starter-loadbalancer`,Feign 就无法进行服务发现和负载均衡调用,从而抛出这个异常。 --- ## ✅ 解决方案 ### ✅ 方法一:添加 `spring-cloud-starter-loadbalancer` 依赖 你需要在你的 `pom.xml`(如果是 Maven 项目)中添加如下依赖: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> ``` 如果你使用的是 Spring Boot 2.7+ 或 Spring Boot 3.x,这是必须的。 #### 示例完整依赖配置: ```xml <dependencies> <!-- OpenFeign 核心依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!-- 负载均衡器 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId> </dependency> <!-- 服务发现客户端(如 Nacos、Eureka 等) --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> ``` --- ### ✅ 方法二:如果你仍在使用 Ribbon(不推荐) 如果你不想使用 LoadBalancer,而是想继续使用 Ribbon(适用于 Spring Cloud 2019.x 及更早版本),可以添加 Ribbon 依赖并禁用 LoadBalancer: ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency> ``` 并在 `application.yml` 中添加: ```yaml spring: cloud: loadbalancer: ribbon: enabled: true ``` ⚠️ **注意**:Spring 官方已弃用 Ribbon,推荐使用新的 LoadBalancer。 --- ## ✅ 方法三:检查 Spring Boot 和 Spring Cloud 的版本兼容性 确保你的 Spring Boot 和 Spring Cloud 版本是兼容的。例如: | Spring Boot 版本 | Spring Cloud 兼容版本 | |------------------|----------------------------| | 2.6.x | 2021.0.x (Jubilee) | | 2.7.x | 2021.1.x | | 3.0.x | 2022.0.x (Kilburn) | | 3.1.x | 2022.1.x | 查看官方兼容性矩阵:[https://spring.io/projects/spring-cloud#overview](https://spring.io/projects/spring-cloud#overview) --- ## ✅ 总结 | 问题 | 原因 | 解决方法 | |------|------|-----------| | Feign 无法负载均衡 | 缺少 `spring-cloud-starter-loadbalancer` | 添加该依赖 | | 项目使用旧版 Ribbon | 新版 Spring Cloud 已弃用 Ribbon | 添加 Ribbon 依赖或升级 | | 版本不兼容 | Spring Boot 与 Spring Cloud 版本不匹配 | 使用兼容版本组合 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值