• SpringCloud-Feign


    1. Feign简介

    1.1 简介

    Feign是Netflix公司开发的一个声明式的REST调用客户端; Ribbon负载均衡、 Hystrⅸ服务熔断是我们Spring Cloud中进行微服务开发非常基础的组件,在使用的过程中我们也发现它们一般都是同时出现的,而且配置也都非常相似,每次开发都有很多相同的代码,因此Spring Cloud基于Netflix Feign整合了Ribbon和Hystrix两个组件,让我们的开发工作变得更加简单, 就像Spring boot是对Spring+ SpringMVC的简化, Spring Cloud Feign对Ribbon负载均衡、 Hystrⅸ服务熔断进行简化,在其基础上进行了进一步的封装,不仅在配置上大大简化了开发工作,同时还提供了一种声明式的Web服务客户端定义方式。使用方式类似Dubbo的使用方式。

    1.2 Feign和Ribbon的联系

    Ribbon是一个基于 HTTP 和 TCP 客户端 的负载均衡的工具。它可以 在客户端配置

    RibbonServerList(服务端列表),使用 HttpClient 或 RestTemplate 模拟http请求,步骤相当繁琐。

    Feign 是在 Ribbon的基础上进行了一次改进,是一个使用起来更加方便的 HTTP 客户端。采用接口的 方式, 只需要创建一个接口,然后在上面添加注解即可 ,将需要调用的其他服务的方法定义成抽象方 法即可, 不需要自己构建http请求。然后就像是调用自身工程的方法调用,而感觉不到是调用远程方 法,使得编写客户端变得非常容易。

    1.3 负载均衡

    Feign中本身已经集成了Ribbon依赖和自动配置,因此我们不需要额外引入依赖,也不需要再注册 RestTemplate 对象。

    2. 能干什么

    feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。

    3. Quick Start

    3.1 创建服务注册中心

    3.1.1 引入依赖坐标

    <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>

    3.1.2 配置application.yml

    server: port: 8080 spring: application: name: eureka-server eureka: instance: hostname: localhost client: # 是否将自己注册到Eureka服务中,本身就是所有无需注册 registerWithEureka: false # 是否从Eureka中获取注册信息 fetchRegistry: false # 客户端与Eureka服务端进行交互的地址 serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

    3.1.3 配置启动类

    添加@EnableEurekaServer 标记为EurekaServer

    @EnableEurekaServer @SpringBootApplication public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }

    3.1.4 启动项目

    浏览器输入localhost:8080访问注册中心

    3.2 创建服务提供者

    3.2.1 引入依赖坐标

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>

    3.2.2 配置application.yml

    这里将port设置为动态传参,主要是想通过设置vm参数来使启动两个端口不同的服务,以便后续通过consumer调用的时候实现负载均衡效果

    server: # 缺省为8010 port: ${port:8010} spring: application: name: provider eureka: client: # eureka server的路径 serviceUrl: defaultZone: http://localhost:8080/eureka/

    3.2.3 创建测试接口

    package com.ldx.provider.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @Value("${server.port}") String port; @GetMapping("hi") public String hi(){ // 通过返回port,使掉用端方便查看调用的是那个端口的服务 return "hi~ my port ===" + port; } // 测试服务调用超时 @GetMapping("hiWithTimeOut") public String hiWithTimeOut() throws InterruptedException { Thread.sleep(10000); return "hi~ my port ===" + port; } }

    3.2.4 配置启动项

    通过idea的复制功能创建两个启动模板,且8011模板通过传port参数实现服务端口的动态替换

    3.2.5 启动服务

    查看注册中心控制台

    服务已注册成功

    测试服务接口

    3.3 创建服务消费者

    3.3.1 引入坐标依赖

    这里之所以将全部的依赖都展示

    1. 因为Netflix 公司2018年已经宣布停止Hystrix的维护,导致springcloud的最新版本即:2020.0.1已经完全废弃了Hystrix

      1. Hystrix替代品为

        文档链接为:Resilience4J

      2. 所以如果要使用Hystrix需使用Hoxton.SR10及以下版本 Hoxton.SR10对应其他服务的版本

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.ldx</groupId> <artifactId>consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR10</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

    3.3.2 配置application.yml

    server: port: 8082 spring: application: name: consumer eureka: client: # eureka server的路径 serviceUrl: defaultZone: http://localhost:8080/eureka/ feign: #开启hystrix熔断机制 hystrix: enabled: true client: config: #配置服务名为provider的相关信息 provider: #打印的日志级别 loggerLevel: FULL #指的是建立连接所用的时间 connectTimeout: 2000 #指的是建立连接后从服务器读取到可用资源所用的时间 readTimeout: 5000 #default代表所有服务 default: #feign客户端建立连接超时时间 connectTimeout: 2000 #feign客户端建立连接后读取资源超时时间 readTimeout: 3000 #feign.client.config.provider.loggerLevel 对应的日志级别需配合logging.level logging: level: com.ldx.consumer.service.HelloService: debug # 配置熔断超时时间 hystrix: command: default: execution: timeout: enabled: true isolation: thread: timeoutInMilliseconds: 50000

    3.3.3 配置启动类

    通过EnableFeignClients开启服务对feign的支持

    package com.ldx.consumer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; @EnableFeignClients @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }

    3.3.4 创建测试接口

    创建测试访问接口

    package com.ldx.consumer.controller; import com.ldx.consumer.service.HelloService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController public class HelloController { @Resource HelloService helloService; @GetMapping("hi") public String hi(){ return helloService.hi(); } @GetMapping("hiWithTimeOut") public String hiWithTimeOut(){ return helloService.hiWithTimeOut(); } }

    @FeignClient:

    • name:指定需要调用的微服务的名称(不分大小写),用于创建Ribbon的负载均衡器。 所以Ribbon会把 provider 解析为注册中心的服务。
    • fallback:指定请求失败时的回退逻辑
    package com.ldx.consumer.service; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "provider",fallback = HelloFallbackImpl.class) public interface HelloService { @GetMapping("hi") String hi(); @GetMapping("hiWithTimeOut") String hiWithTimeOut(); }

    请求失败回退处理

    package com.ldx.consumer.service; import org.springframework.stereotype.Component; @Component public class HelloFallbackImpl implements HelloService { @Override public String hi() { return "远程服务不可用,请稍后重试。。。。。"; } @Override public String hiWithTimeOut() { return "请求超时。"; } }

    3.3.5 启动项目

    查看注册中心控制台

    服务注册已成功

    测试接口

    第一次访问:

    第二次访问:

    测试请求服务超时

    打印堆栈信息如下:

    当直接把服务提供者关闭后,请求返回结果也是熔断返回的信息,符合预期。

    4. 客户端Hystrix整合

    springcloud每个版本的差异比较大,有的低版本可能需要显性的引用hystrix依赖

    当前使用的Hoxton.SR10版本不需要引用,因为已经被feign整合了

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

    4.1 熔断器使用

    在网络请求时,可能会出现异常请求,如果还想再异常情况下使系统可用,那么就需要容错处理,比如:网络请求超时时给用户提示“稍后重试”或使用本地快照数据等等。

    Spring Cloud Feign就是通过Fallback实现的,有两种方式:

    1. @FeignClient.fallback = xxxFeignFallback.class指定一个实现Feign接口的实现类。
    2. @FeignClient.fallbackFactory = xxxFeignFactory.class指定一个实现FallbackFactory工厂接口类

    注意:feign的注解@FeignClient:fallbackFactory与fallback方法不能同时使用,这个两个方法其实都类似于Hystrix的功能,当网络不通时返回默认的配置数据。

    4.2 配置文件配置

    在application.properties 启用hystrix

    feign: #开启hystrix熔断机制 hystrix: enabled: true

    请务必注意,从Spring Cloud Dalston开始,Feign默认是不开启Hystrix的。

    因此,如使用Dalston以及以上版本请务必额外设置属性:feign.hystrix.enabled=true,否则 断路器不会生效。

    Spring Cloud Angel/Brixton/Camden中,Feign默认都是开启Hystrix的。

    4.3 fallback 实现

    创建HelloFallbackImpl的回调实现,由spring创建使用@Component(其他的注册也可以)注解

    HystrixTargeter.targetWithFallback方法实现了@FeignClient.fallback处理逻辑,通过源码可以知道HelloFallbackImpl回调类是从Spring容器中获取的,所以HelloFallbackImpl由spring创建。

    4.3.1 HelloService 接口类

    package com.ldx.consumer.service; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "provider",fallback = HelloFallbackImpl.class) public interface HelloService { @GetMapping("hi") String hi(); @GetMapping("hiWithTimeOut") String hiWithTimeOut(); }

    4.3.2 HelloFallbackImpl回退类

    package com.ldx.consumer.service; import org.springframework.stereotype.Component; @Component public class HelloFallbackImpl implements HelloService { @Override public String hi() { return "远程服务不可用,请稍后重试。。。。。"; } @Override public String hiWithTimeOut() { return "请求超时。"; } }

    4.3.3 验证

    关闭服务提供者后测试

    4.4 FallbackFactory工厂

    上面的实现方式简单,但是获取不到HTTP请求错误状态码和信息 ,这时就可以使用工厂模式来实现Fallback

    同样工厂实现类也要交由spring管理。

    4.4.1 HelloService 接口类

    fallbackFactory 属性 指定了 自定义的回退工厂

    package com.ldx.consumer.service; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; @FeignClient(name = "provider",fallbackFactory = HelloFallbackFactory.class) public interface HelloService { @GetMapping("hi") String hi(); @GetMapping("hiWithTimeOut") String hiWithTimeOut(); }

    4.4.2 HelloFallbackFactory回退类

    注意 实现的是feign.hystrix.FallbackFactory

    package com.ldx.consumer.service; import feign.hystrix.FallbackFactory; import org.springframework.stereotype.Component; @Component public class HelloFallbackFactory implements FallbackFactory<HelloService> { @Override public HelloService create(Throwable throwable) { return new HelloService() { @Override public String hi() { return "请求失败~ error msg:" + throwable.getMessage(); } @Override public String hiWithTimeOut() { return "请求超时~ error msg:" + throwable.getMessage(); } }; } }

    4.4.3 验证

    关闭服务提供者后测试

    5. 自定义ErrorDecoder

    ErrorDecoder接口处理请求错误信息,默认实现ErrorDecoder.Default抛出FeignException异常

    FeignException.status 方法返回HTTP状态码,FallbackFactory.create默认情况下可以强制转换成FeignException异常这样就可以获取到HTTP状态码了。

    5.1 FeignErrorDecoder

    自定义FeignErrorDecoder

    @Configuration public class FeginErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { ServiceException serviceException = new ServiceException(); serviceException.setMethod(methodKey); if (response.status() >= 400 && response.status() <= 499) { serviceException.setCode(response.status()); serviceException.setErrorMessage(response.reason()); serviceException.setMessage("页面或者参数错误"); } if (response.status() >= 500 && response.status() <= 599) { serviceException.setCode(response.status()); serviceException.setErrorMessage(response.reason()); serviceException.setMessage("服务器错误"); } return serviceException; } } class ServiceException extends RuntimeException { private int code; private String message; private String method; private String errorMessage; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String getLocalizedMessage() { return "错误码:" + code + ",错误信息:" + message + ",方法:" + method + ",具体错误信息:" + errorMessage; } }

    消费者提供请求报错的方法,供调用者调用

    @GetMapping("hiWithError") public String hiWithError() { int a = 1/0; return "hi~ my port ===" + port; }

    5.2 流程

    在Feign客户端发生http请求层面(提供者业务代码报错)的错误时会调用decode方法。在decode方法中实现自定义的错误处理,当出现异常时首先会通过FeginErrorDecoder进行异常的封装,然后会调用HelloFallbackFactory进行异常的回调处理

    消费者调用结果如下

    6. 扩展

    6.1 Feign使用HttpClient

    Feign在默认情况下使用的是JDK原生URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。我们可以用Apache的HTTP Client替换Feign原始的http client,从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换。

    6.1.2 导入POM依赖

    <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> <version>11.0</version> </dependency>

    6.1.3 配置文件配置

    feign: httpclient: enabled: true

    主要:这个配置可加可不加,在该版本中默认为true,可以不加,在HttpClientFeignLoadBalancedConfiguration源码中有的配置默认为true

    @ConditionalOnProperty( value = {"feign.httpclient.enabled"}, matchIfMissing = true )

    6.1.4 验证

    首先在工程配置文件中,将配置项 feign.httpclient.enabled 的值,设置为 false 。然后,在HttpClientFeignLoadBalancedConfiguration 的 feignClient(…)方法内的某行打上断点,重新启动项目,注意观察会发现,整个启动过程中,断点没有被命中。接下来,将配置项 feign.httpclient.enabled 的值设置为 true,再一次启动项目,断点被命中。由此,可以验证 HttpClientFeignLoadBalancedConfiguration 自动配置类被启动。

    6.2 在feign请求之前操作

    feign组件提供了请求操作接口RequestInterceptor,实现之后对apply函数进行重写就能对request进行修改,包括header和body操作。

    @Component public class TokenRequestInterceptor implements RequestInterceptor { @Override public void apply(feign.RequestTemplate template) { String method = template.method(); String url = template.url(); System.out.println("调用方法:" + method + ",URL地址:" + url); } }

    6.3 请求压缩

    Spring Cloud Feign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。我们只需通过下面两个参数设置,就能开启请求与响应的压缩功能:

    feign.compression.request.enabled=true feign.compression.response.enabled=true

    同时,我们还能对请求压缩做一些更细致的设置,比如下面的配置内容指定了压缩的请求数据类型,并设置了压缩的大小下限,只有超过这个大小的请求才会对其进行压缩。

    feign.compression.request.enabled=true feign.compression.request.mime-types=text/xml,application/xml,application/json feign.compression.request.min-request-size=2048

    上述配置的feign.compression.request.nime-types和feign.compression.requestmin-request-size均为默认值。

    6.4 日志配置

    Spring Cloud Feign在构建被@FeignClient注解修饰的服务客户端时,会为每一个客户端都创建一个feign的请求细节。可以在application.properties文件中使用logging.level.的参数配置格式来开启指定Feign客户端的DEBUG日志,其中为Feign客户端引用全路径路径,比如针对本文中我们实现的HelloService可以如下配置开启:

    logging.level.com.springcloud.user.service.HelloService=DEBUG

    但是,只是添加了如上配置,还无法实现对DEBUG日志的输出。这时由于Feign客户端默认对Logger.Level对象定义为NONE级别,该界别不会记录任何Feign调用过程中对信息,所以我们需要调整它对级别,针对全局对日志级别,可以在应用主类中直接假如Logger.Level的Bean创建,具体如下:

    @Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; }

    在调整日志级别为FULL之后,我们调用接口测试,查看日志

    2021-03-02 20:12:56.169 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] ---> GET http://provider/hi HTTP/1.1 2021-03-02 20:12:56.169 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] Accept-Encoding: gzip 2021-03-02 20:12:56.169 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] Accept-Encoding: deflate 2021-03-02 20:12:56.169 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] ---> END HTTP (0-byte body) 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] <--- HTTP/1.1 200 (5ms) 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] connection: keep-alive 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] content-length: 19 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] content-type: text/plain;charset=UTF-8 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] date: Tue, 02 Mar 2021 12:12:56 GMT 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] keep-alive: timeout=60 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] 2021-03-02 20:12:56.175 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] hi~ my port ===8011 2021-03-02 20:12:56.176 DEBUG 4186 --- [trix-provider-2] com.ldx.consumer.service.HelloService : [HelloService#hi] <--- END HTTP (19-byte body)

    6.4.1 fegin日志级别

    对于Feign的Logger级别主要有下面4类,可根据实际需要进行调整使用。

    NONE:不记录任何信息。

    BASIC:仅记录请求方法、URL以及响应状态码和执行时间。

    HEADERS:出了记录BASIC级别的信息之外,还会记录请求和响应的头信息。

    FULL:记录所有请求与响应的细节,包括头信息、请求体、元数据等。

    7. FeignClient注解的一些属性

    属性名 默认值 作用 备注
    value 空字符串 调用服务名称,和name属性相同
    serviceId 空字符串 服务id,作用和name属性相同 已过期
    name 空字符串 调用服务名称,和value属性相同
    contextId 空字符串 参考下方介绍
    url 空字符串 全路径地址或hostname,http或https可选 url用于配置指定服务的地址,相当于直接请求这个服务,不经过Ribbon的服务选择。
    decode404 FALSE 配置响应状态码为404时是否应该抛出FeignExceptions
    configuration {} 自定义当前feign client的一些配置 参考FeignClientsConfiguration
    fallback void.class 熔断机制,调用失败时,走的一些回退方法,可以用来抛出异常或给出默认返回数据。 底层依赖hystrix,启动类要加上@EnableHystrix
    fallbackFactory void.class 和fallback功能相似,只不过fallbackFactory中有报错的Throwable信息
    path 空字符串 自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping
    primary TRUE primary对应的是@Primary注解,默认为true,官方这样设置也是有原因的。当我们的Feign实现了fallback后,也就意味着Feign Client有多个相同的Bean在Spring容器中,当我们在使用@Autowired进行注入的时候,不知道注入哪个,所以我们需要设置一个优先级高的,@Primary注解就是干这件事情的。
    qualifier 介绍如下

    7.1 contextId

    比如我们有个user服务,但user服务中有很多个接口,我们不想将所有的调用接口都定义在一个类中,比如:

    Client 1

    @FeignClient(name = "optimization-user") public interface UserRemoteClient { @GetMapping("/user/get") public User getUser(@RequestParam("id") int id); }

    Client 2

    @FeignClient(name = "optimization-user") public interface UserRemoteClient2 { @GetMapping("/user2/get") public User getUser(@RequestParam("id") int id); }

    这种情况下启动就会报错了,因为Bean的名称冲突了,具体错误如下:

    Description: The bean 'optimization-user.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

    解决方案可以增加下面的配置,作用是允许出现beanName一样的BeanDefinition。

    spring.main.allow-bean-definition-overriding=true

    另一种解决方案就是为每个Client手动指定不同的contextId,这样就不会冲突了。

    上面给出了Bean名称冲突后的解决方案,下面来分析下contextId在Feign Client的作用,在注册Feign Client Configuration的时候需要一个名称,名称是通过getClientName方法获取的:

    String name = getClientName(attributes); registerClientConfiguration(registry, name, attributes.get("configuration"));
    private String getClientName(Map<String, Object> client) { if (client == null) { return null; } String value = (String) client.get("contextId"); if (!StringUtils.hasText(value)) { value = (String) client.get("value"); } if (!StringUtils.hasText(value)) { value = (String) client.get("name"); } if (!StringUtils.hasText(value)) { value = (String) client.get("serviceId"); } if (StringUtils.hasText(value)) { return value; } throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName()); }

    可以看到如果配置了contextId就会用contextId,如果没有配置就会去value然后是name最后是serviceId。默认都没有配置,当出现一个服务有多个Feign Client的时候就会报错了。

    其次的作用是在注册FeignClient中,contextId会作为Client 别名的一部分,如果配置了qualifier优先用qualifier作为别名。

    7.2 qualifier

    qualifier对应的是@Qualifier注解,使用场景跟上面的primary关系很淡,一般场景直接@Autowired直接注入就可以了。

    如果我们的Feign Client有fallback实现,默认@FeignClient注解的primary=true, 意味着我们使用@Autowired注入是没有问题的,会优先注入你的Feign Client。

    如果你鬼斧神差的把primary设置成false了,直接用@Autowired注入的地方就会报错,不知道要注入哪个对象。

    解决方案很明显,你可以将primary设置成true即可,如果由于某些特殊原因,你必须得去掉primary=true的设置,这种情况下我们怎么进行注入,我们可以配置一个qualifier,然后使用@Qualifier注解进行注入,示列如下:

    Feign Client定义

    @FeignClient(name = "optimization-user", path="user", qualifier="userRemoteClient") public interface UserRemoteClient { @GetMapping("/get") public User getUser(@RequestParam("id") int id); }

    Feign Client注入

    @Autowired @Qualifier("userRemoteClient") private UserRemoteClient userRemoteClient;

    __EOF__

    本文作者张铁牛
    本文链接https://www.cnblogs.com/ludangxin/p/16051873.html
    关于博主:评论和私信会在第一时间回复。或者直接私信我。
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    你还不会写API文档吗
    水库安全监测方案(实时数据采集、高速数据传输)
    Springboot处理Long类型传给前端导致精度丢失问题
    Vue3中watchEffect侦听器的使用
    C++ Visual Studio 2022 中的改进、行为更改和错误修复
    Vuex简介
    基于Python和lammps模拟聚合物交联过程
    JavaScript系列从入门到精通系列第二十篇:使用工厂方法创建JavaScript对象,JavaScript构造函数详解,JavaScript类概念的介绍
    论文翻译:2021_Joint Online Multichannel Acoustic Echo Cancellation, Speech Dereverberation and Source Separation
    Delphi终极版速扩展VCL应用程序
  • 原文地址:https://www.cnblogs.com/ludangxin/p/16051873.html