OpenFeign使用总结

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

本文基于springcloud2021.0.5、springboot 2.6.13,spring-cloud-starter-openfeign 3.1.8

基本使用

使用feign首先要了解@EnableFeignClients@FeignClient这两个注解:

@EnableFeignClients

  1. @EnableFeignClients 默认扫描 xxxxApplication启动入口 所在包路径下的@FeignClient bean,若无法扫描到,可以在使用Feign调用外部模块的api时候,需要在引用服务中 xxxxApplication 中的 @EnableFeignClients(basePackages = "xxx.xx.xx") 添加外部包需要扫描FeignClient的路径,否则无法注入Bean
  2. @FeignClient 声明的类,使用 spring.application.name 作为 name配置 @FeignClient(name=“xxxx”),如果想保留 context-path , 则需要配置 path 属性 ,如:@FeignClient(name=“xxxx” , path=“xxxx”),这个path值一般是context-path
  3. @FeignClient接口对应的实现类,需要使用 @RestController注解 声明
  4. 不支持@GetMapping,@PostMapping等注解,参数要加 @RequestParam(“xxx”)
  5. FeignClient 调用,实质是httpClient调用 ,若我们暴露的接口api,声明了对应的 http mapper 信息,在调用方调用时候,通过代理 发起了 http请求,到服务提供方对应的http服务上去,所以在服务提供方的接口,可以使用 @RestController来声明接口的 实现,否则调用方无法找到 http 对应的路径,会报404 ; 或者 根据 api 声明的http 信息,构建一个 Controller ,再来调用接口的实现类,但是这样不太方便;

@FeignClient

@FeignClient 标记要用Feign来请求的接口

@FeignClient(name = "payment-service", path = "/api/payment")
public interface FeignPaymentService {

    @PostMapping("/create")
    Map<String, Object> createPayment(Map<String, Object> map);
}

定义被调用方接口,返回自身的端口号

@RestController
@RequestMapping("/api/payment")
public class PaymentController {

    @Value("${server.port}")
    private Integer port;

    @PostMapping("/create")
    public Map<String, Object> create(Map<String, Object> map) {
        map.put("port", this.port);
        return map;
    }
}

name/value

指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现

contextId

如果提供了bean名称,则此名称将用作bean的名称而不是默认的name,但它不会被用作服务ID。

这个ID用于在Spring容器中区分不同的Feign客户端实例。在微服务架构中,一个服务可能会调用多个其他服务,而这些服务可能由不同的Feign客户端代理。通过使用contextId,可以确保这些Feign客户端在Spring容器中具有唯一的标识,从而避免潜在的冲突和混淆。

默认情况下每个FeignClient注册进容器的Bean名称是name值+“.FeignClientSpecification”,比如payment-service.FeignClientSpecification

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

@FeignClient(name = "payment-service")
public interface PaymentClient1 {
	@GetMapping("/api/payment/create")
	public Map<String, Object> createPayment(Map<String, Object> map);
}

@FeignClient(name = "payment-service")
public interface PaymentClient2 {
	@GetMapping("/api/payment/getById")
	public Map<String, Object> getPaymentById(@RequestParam("id") int id);
}

此时就会冲突,启动时就会报错
The bean ‘payment-service.FeignClientSpecification’ could not be registered. A bean with that name has already been defined and overriding is disabled.

此时就可以用这个conextId,加了这个注册的bean名称就是conextId值拼上.FeignClientSpecification

或者通过spring.main.allow-bean-definition-overriding=true允许出现beanName一样的BeanDefinition

qualifiers

FeignClient的@Qualifiers值,如果qualifier()和qualifiers()同时存在,则使用qualifiers()配置, 除非qualifiers()返回空或者只包含null或者空白字符,这时会使用qualifier()配置,如果此时qualifier()不存在,则会取默认值contextId + “FeignClient”.

如果有两个类型相同的FeignClient(类型相同,必须在不同的包),可以通过指定qualifiers注入指定的FeignClient

@FeignClient(contextId = "pc1", name = "payment-service", qualifiers = {"p1"})
public interface FeignPaymentService

@FeignClient(contextId = "pc2", name = "payment-service", qualifiers = {"p2"})
public interface FeignPaymentService

@Autowired
@Qualifier("p1")
FeignPaymentService paymentService;

url

一般用于调试,可以手动指定@FeignClient调用的地址 decode404:当发生http404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException

decode404

decode404控制当Feign客户端接收到HTTP 404响应时的行为。

当decode404设置为true时,如果Feign客户端收到了HTTP 404响应,它会尝试调用配置的Decoder来解码这个响应体。这意味着,尽管请求的资源在服务器上不存在(即404状态),但Feign仍然会尝试将响应体转换为客户端期望的格式,以便进一步处理或抛出更具体的异常。
当decode404设置为false(默认值)时,如果Feign客户端收到HTTP 404响应,它将不会尝试解码响应体,而是直接抛出一个FeignException异常。这通常用于快速失败,因为404错误通常表示客户端请求了一个不存在的资源,无需进一步处理响应体。
配置decode404的行为可以根据应用的需求和错误处理策略进行调整。例如,如果应用需要根据404响应体中的信息来执行特定的逻辑(比如记录日志、返回特定的用户消息等),那么可以将decode404设置为true。否则,如果应用对404错误的处理较为简单,只是需要知道资源不存在,那么保持decode404的默认值(false)可能更为合适。

configuration

Feign配置类,可以自定义Feign的feign.codec.Encoder、feign.codec.Decoder、LogLevel、feign.Contract

fallback

指定发送异常调用或者超时时应该调用那个类来执行备用方法
定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口

fallbackFactory

用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的异常熔断处理逻辑,减少重复的代码

path

path表示当前FeignClient的路径的统一前缀,这个 path 就是对应被调用服务的 context-path 值。如果保留服务的 servlet.context-path 配置,则需要指定path值为 servlet.context-path 值

例如,要调用的服务URL是:http://localhost:8080/remote/test/show,其中 /remote 为该服务的 context-path,那么这个时候 path 属性就应该为 /remote,即path+方法上的Mapping路径 = 请求的接口地址

primary

是否将Feign代理标记为主bean。默认为true

使用配置

单个 Feign 配置方式使用@FeignClient的configuration属性,指定一个配置类,如下所示

@Configuration
public class FooConfiguration {
    
    @Bean
    public Contract feignContract() {
        return new feign.Contract.Default();
    }
	
	// 拦截器配置
    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("user", "password");
    }
}

单个配置(yml形式),通过 feignName 应用于指定的 FeignClient 实例。

feign:
  client:
    config:
      feignName: # 指定Feign的名称,及@FeignClient的name属性值
      	# HTTP 连接超时时间
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        errorDecoder: com.example.SimpleErrorDecoder
        # 重试策略
        retryer: com.example.SimpleRetryer
        # 请求拦截器
        requestInterceptors:
          - com.example.FooRequestInterceptor
          - com.example.BarRequestInterceptor
        # 编码器
        encoder: com.example.SimpleEncoder
        # 解码器
        decoder: com.example.SimpleDecoder

请求拦截器

feign 它自带的拦截器 feign.RequestInterceptor 只能拦截请求, 做一些修改请求头、请求参数之类的动作。没办法将请求和响应一起拦截。

在原生 feign 使用过程中,拦截器通过FeignBuilder添加

Bank bank = Feign.builder()
             .decoder(accountDecoder)
             .requestInterceptor(new ForwardedForInterceptor())
             .target(Bank.class, "https://api.examplebank.com");

使用Spring的话,可以直接RequestInterceptor注册进容器

@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        String path = requestTemplate.path();
        String url = requestTemplate.url();
    }
}

请求重试

feign接口调用超时默认会有重试机制
通过配置feign.Retryer实例可以修改重试的时间间隔,最大重试次数等

@Bean
public Retryer feignRetryer(){
	// 参数依次为:时间间隔, 最大时间间隔, 最大重试次数
    return new Retryer.Default(100, 1000, 4);
}

负载均衡

同时运行4个payment-service服务
在这里插入图片描述
注册中心中可以看到有4个payment-service服务
在这里插入图片描述

可以看到每次调用返回的端口号都不同,同时8001-8004轮流出现

底层就是这个依赖起作用

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

问题记录

1.接口可以调通,但是调用方这边报404错误
feign.FeignException$NotFound: [404] during [POST] to [http://payment-service/api/payment/create] [FeignPaymentService#createPayment(Map)]

TODO

引用\[1\]:在非微服务场景中,也可以单独使用Spring Boot的OpenFeign组件。OpenFeign是一个用于远程服务访问的RESTful客户端,它可以简化远程服务的调用过程,使其更像是调用本地接口。OpenFeign底层通信的HTTP客户端默认使用的是OkHttp。所以,即使在非微服务场景中,你也可以使用OpenFeign来访问其他服务,而不需要手动封装HttpClient或者RestTemplate工具类。\[1\] 要在Spring Boot项目中使用OpenFeign,你需要在项目的pom.xml或者build.gradle文件中添加OpenFeign的依赖。例如,使用Gradle的写法可以是: ``` implementation group: 'io.github.openfeign', name: 'feign-core', version: "11.1" implementation group: 'io.github.openfeign', name: 'feign-jackson', version: "11.1" ``` 然后,你可以编写远程服务的示例代码,使用OpenFeign来调用其他服务的接口。通过定义接口的方式,你可以像调用本地接口一样调用远程服务的方法。\[2\] 总结来说,Spring Boot的OpenFeign组件可以在非微服务场景中单独使用,它提供了一种简化远程服务调用的方式,使其更加简单和方便。你只需要添加OpenFeign的依赖,并编写相应的接口代码即可。\[1\] #### 引用[.reference_title] - *1* *3* [springboot学习(五十一) springboot使用openfeign实现调用本地接口访问远程服务](https://blog.csdn.net/u011943534/article/details/119516530)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring Boot中单独使用OpenFeign代替HttpClient/RestTemplate](https://blog.csdn.net/Hatakefiftyfifty/article/details/124790463)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值