Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 代码实践

基于 spring-boot-starter-parent 2.1.9.RELEASE, spring-cloud-openfeign 2.1.3.RELEASE

引子

Feign 是一个声明式、模板化的HTTP客户端,简化了系统发起Http请求。创建它时,只需要创建一个接口,然后加上FeignClient注解,使用它时,就像调用本地方法一样,作为开发者的我们完全感知不到这是在调用远程的方法,也感知不到背后发起了HTTP请求:

/**
 * @author axin
 * @suammry xx 客户端
 */
@FeignClient(value = "xxClient",url = "${xx.host:www.axin.com}")
public interface DemoClient {

  @PostMapping(value = "/xxx/url", headers = "Content-Type=application/json"})
  yourResponse requestHTTP(@RequestBody JSONObject param);

}

上述的代码就是一个定义一个Feign HTTP 客户端,在其他类中只需要 @Autowired DemoClient,就可以像调用本地方法一样发起HTTP请求。

介绍就到这,接下来进入主题,因为 FeignClient 将发起HTTP请求与解析返回报文都做了包装,如果你的业务场景需要定制一些调用机制,比如:

  • 我想在发起请求响应超时失败时自动重试 —— 自定义重试机制
  • 我想单独对某些异常的HTTP状态码特殊处理 —— 自定义ErrorDecoder
  • 服务端接口需要验证签名,所以我方在发起请求时要生成签名然后传过去 —— 定义 Fegin 拦截器

基于此,本文就以上述3个需求场景为例来介绍如何自定义 FeignClient 的配置

FeignClient的默认配置类

Feign Client 默认的配置类为 FeignClientsConfiguration, 这个类在 spring-cloud-netflix-core 的 jar 包下。

默认注入了很多 Feign 相关的配置Bean,包括FeignRetryer、 FeignLoggerFactory 和 FormattingConversionService 等。另外,Decoder、Encoder和 Contract 这3个类在没有Bean被注入的情况下,会自动注入默认配置的 Bean,即ResponseEntity Decoder、SpringEncoder 和 SpringMvcContract。

如果你不知道如何自己定义配置时,不放点进去看看人家默认配置是如何实现的。这里就不晒源码了。

FeignClient 注解参数

每个注解参数都做了注释,详情请见下方源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {

	/**
	 * 指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
	 */
	@AliasFor("name")
	String value() default "";
	@Deprecated
	String serviceId() default "";
	@AliasFor("value")
	String name() default "";

	/**
	 * Sets the <code>@Qualifier</code> value for the feign client.
	 * 这个bean在应用上下文中的名字为接口的全限定名,你也可以使用 @FeignClient 注解中的 qualifier 属性给bean指定一个别名
	 */
	String qualifier() default "";

	/**
	 * url地址
	 */
	String url() default "";

	/**
	 * 当发生404错误,如果该字段为true,会调用decoder进行解码,否则抛出FeignException
	 */
	boolean decode404() default false;

	/**
	 * 指定FeignClient 的配置类,优先级最高,默认的配置类为 FeignClientsConfiguration类
	 */
	Class<?>[] configuration() default {};

	/**
	 * 配置熔断器的处理类
	 */
	Class<?> fallback() default void.class;

	/**
	 * 工厂类,用于生产fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复代码
	 */
	Class<?> fallbackFactory() default void.class;

	/**
	 * 定义统一的路径前缀
	 */
	String path() default "";

	/**
	 * Whether to mark the feign proxy as a primary bean. Defaults to true.
	 */
	boolean primary() default true;
}

自定义Feign配置类

在 Spring Cloud 中,你可以通过 @FeignClient 注解声明额外的配置(比 FeignClientsConfiguration 级别高)去控制feign客户端,以一开始的feign接口为例:

/**
 * @author axin
 * @suammry xx 客户端
 */
@FeignClient(value = "xxClient",url = "${xx.host:www.axin.com}",configuration = MyConfiguration.class)
public interface DemoClient {

  @PostMapping(value = "/xxx/url", headers = "Content-Type=application/json"})
  yourResponse requestHTTP(@RequestBody JSONObject param);
}

在上面这个示例中,feign客户端在MyConfiguration中的配置将会覆盖FeignClientsConfiguration中的配置。

要注意的是: MyConfiguration不需要使用@Configuration注解。如果加上了,它将全局生效。

Retryer-重试机制的自定义

/**
 * @author axin
 * @summary fegin 客户端的自定义配置
 */
public class MyConfiguration {

  /**
   * 自定义重试机制
   * @return
   */
  @Bean
  public Retryer feignRetryer() {
    //fegin提供的默认实现,最大请求次数为5,初始间隔时间为100ms,下次间隔时间1.5倍递增,重试间最大间隔时间为1s,
    return new Retryer.Default();
  }
}

ErrorDecoder-错误解码器的自定义

当feign调用返回HTTP报文时,会触发这个方法,方法内可以获得HTTP状态码,可以用来定制一些处理逻辑等等。

/**
 * @author axin
 * @summary fegin 客户端的自定义配置
 */
@Slf4j
public class MyConfiguration {

  /**
   * 自定义重试机制
   * @return
   */
  @Bean
  public Retryer feignRetryer() {
    //最大请求次数为5,初始间隔时间为100ms,下次间隔时间1.5倍递增,重试间最大间隔时间为1s,
    return new Retryer.Default();
  }

  @Bean
  public ErrorDecoder feignError() {
    return (key, response) -> {
      if (response.status() == 400) {
        log.error("请求xxx服务400参数错误,返回:{}", response.body());
      }

      if (response.status() == 409) {
        log.error("请求xxx服务409异常,返回:{}", response.body());
      }

      if (response.status() == 404) {
        log.error("请求xxx服务404异常,返回:{}", response.body());
      }

      // 其他异常交给Default去解码处理
      // 这里使用单例即可,Default不用每次都去new
      return new ErrorDecoder.Default().decode(key, response);
    };
  }

}

采用了lambda的写法,response变量是Response类型,通过status()方法可以拿到返回的HTTP状态码,body()可以获得到响应报文。

Feign拦截器实践

拦截器在请求发出之前执行,在拦截器代码里可以修改请求参数,header等等,如果你有签名生成的需求,可以放在拦截器中来实现

/**
 * @author axin
 * @summary fegin 客户端的自定义配置
 */
@Slf4j
public class MyConfiguration {

  /**
   * 自定义重试机制
   * @return
   */
  @Bean
  public Retryer feignRetryer() {
    //最大请求次数为5,初始间隔时间为100ms,下次间隔时间1.5倍递增,重试间最大间隔时间为1s,
    return new Retryer.Default();
  }

  @Bean
  public ErrorDecoder feignError() {
    return (key, response) -> {
      if (response.status() == 400) {
        log.error("请求xxx服务400参数错误,返回:{}", response.body());
      }

      if (response.status() == 409) {
        log.error("请求xxx服务409异常,返回:{}", response.body());
      }

      if (response.status() == 404) {
        log.error("请求xxx服务404异常,返回:{}", response.body());
      }

      // 其他异常交给Default去解码处理
      // 这里使用单例即可,Default不用每次都去new
      return new ErrorDecoder.Default().decode(key, response);
    };
  }

   /**
   * fegin 拦截器
   * @return
   */
  @Bean
  public RequestInterceptor cameraSign() {
    return template -> {

      // 如果是get请求
      if (template.method().equals(Request.HttpMethod.GET.name())) {
        //获取到get请求的参数
        Map<String, Collection<String>> queries = template.queries();
      }

      //如果是Post请求
      if (template.method().equals(Request.HttpMethod.POST.name())) {
        //获得请求body
        String body = template.requestBody().asString();
        JSONPObject request = JSON.parseObject(body, JSONPObject.class);
      }

      //Do what you want... 例如生成接口签名

      String sign = "根据请求参数生成的签名";
      //放入url?之后
      template.query("sign", sign);

      //放入请求body中
      String newBody = "原有body" + sign;
      template.body(Request.Body.encoded(newBody.getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8));
    };
  }
}

可以看到代码中给了如何获取请求参数和修改请求参数的示例。

总结

小结一下,对于开头提出的场景:

  1. 我想在发起请求响应超时失败时自动重试 —— 自定义重试机制
  2. 我想单独对某些异常的HTTP状态码特殊处理 —— 自定义ErrorDecoder
  3. 服务端接口需要验证签名,所以我方在发起请求时要生成签名然后传过去 —— 定义 Fegin 拦截器

给出了自定义 feign 配置的方式实现的样例代码,希望对你有用,如果有更好的方式简化HTTP请求,欢迎留言分享~

参考链接

重新定义Spring Cloud实战

Spring Cloud Netflix官方文档

总结

到此这篇关于Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 实践的文章就介绍到这了,更多相关Spring Cloud Feign 自定义配置内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解spring cloud feign踩坑记录

    1:多客户端时,feign接口抽取到公共jar中,此时,客户端的启动类上需要对该jar中feign所在的包进行扫描,要在spring和feign中同时注册,否则启动时会报:"Consider defining a bean of type '******Feign' in your configuration." @SpringBootApplication @EnableTransactionManagement @EnableDiscoveryClient @ComponentSc

  • Spring Cloud使用Feign实现Form表单提交的示例

    之前,笔者写了<使用Spring Cloud Feign上传文件>.近日,有同事在对接遗留的Struts古董系统,需要使用Feign实现Form表单提交.其实步骤大同小异,本文附上步骤,算是对之前那篇的补充. 添加依赖: <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>

  • Spring Cloud Feign实例讲解学习

    前面博文搭建了一个Eureka+Ribbon+Hystrix的框架,虽然可以基本满足服务之间的调用,但是代码看起来实在丑陋,每次客户端都要写一个restTemplate,为了让调用更美观,可读性更强,现在我们开始学习使用Feign. Feign包含了Ribbon和Hystrix,这个在实战中才慢慢体会到它的意义,所谓的包含并不是Feign的jar包包含有Ribbon和Hystrix的jar包这种物理上的包含,而是Feign的功能包含了其他两者的功能这种逻辑上的包含.简言之:Feign能干Ribb

  • Spring Cloud如何使用Feign构造多参数的请求

    本节我们来探讨如何使用Feign构造多参数的请求.笔者以GET以及POST方法的请求为例进行讲解,其他方法(例如DELETE.PUT等)的请求原理相通,读者可自行研究. GET请求多参数的URL 假设我们请求的URL包含多个参数,例如http://microservice-provider-user/get?id=1&username=张三 ,要如何构造呢? 我们知道,Spring Cloud为Feign添加了Spring MVC的注解支持,那么我们不妨按照Spring MVC的写法尝试一下:

  • 详解Spring Cloud Feign 熔断配置的一些小坑

    1.在使用feign做服务调用时,使用继承的方式调用服务,加入Hystrix的熔断处理fallback配置时,会报错,已解决. 2.使用feign默认配置,熔断不生效,已解决. 最近在做微服务的学习,发现在使用feign做服务调用时,使用继承的方式调用服务,加入Hystrix的熔断处理fallback配置时,会报错,代码如下: @RequestMapping("/demo/api") public interface HelloApi { @GetMapping("user/

  • Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 代码实践

    基于 spring-boot-starter-parent 2.1.9.RELEASE, spring-cloud-openfeign 2.1.3.RELEASE 引子 Feign 是一个声明式.模板化的HTTP客户端,简化了系统发起Http请求.创建它时,只需要创建一个接口,然后加上FeignClient注解,使用它时,就像调用本地方法一样,作为开发者的我们完全感知不到这是在调用远程的方法,也感知不到背后发起了HTTP请求: /** * @author axin * @suammry xx 客

  • Spring Cloud Feign组成配置过程解析

    Feign的组成 接口 作用 默认值 Feign.Builder Feign的入口 Feign.Builder Client Feign底层用什么去请求 和Ribbon配合时:LoadBalancerFeignClient 不和Ribbon配合时:Fgien.Client.Default Contract 契约,注解支持 SpringMVCContract Encoder 解码器,用于将独享转换成HTTP请求消息体 SpringEncoder Decoder 编码器,将相应消息体转成对象 Res

  • Spring Cloud Feign高级应用实例详解

    这篇文章主要介绍了Spring Cloud Feign高级应用实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.使用feign进行服务间的调用 Spring boot2X Consul如何使用Feign实现服务调用 2.开启gzip压缩 Feign支持对请求与响应的压缩,以提高通信效率,需要在服务消费者配置文件开启压缩支持和压缩文件的类型 添加配置 feign.compression.request.enabled=true feig

  • Spring Cloud超详细i讲解Feign自定义配置与使用

    目录 日志配置 Basic 认证配置 超时时间配置 客户端组件配置 GZIP压缩配置 继承特性 多参数请求构造 日志配置 有时候我们遇到 Bug,比如接口调用失败.参数没收到等 问题,或者想看看调用性能,就需要配置 Feign 的日志了, 以此让 Feign 把请求信息输出来. 首先定义一个配置类,代码如下所示. package com.by.config; import feign.Logger; import org.springframework.context.annotation.Be

  • Spring cloud Feign 深度学习与应用详解

    简介 Spring Cloud Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单.Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解,就可以定义好HTTP请求的参数.格式.地址等信息.Feign会完全代理HTTP请求,开发时只需要像调用方法一样调用它就可以完成服务请求及相关处理.开源地址:https://github.com/OpenFeign/feign.Feign整合了Ribbon负载和Hystrix熔断,可以不再需要显式地

  • Spring Cloud Feign组件实例解析

    这篇文章主要介绍了Spring Cloud Feign组件实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 采用Spring Cloud微服务框架后,经常会涉及到服务间调用,服务间调用采用了Feign组件. 由于之前有使用dubbo经验.dubbo的负载均衡策略(轮训.最小连接数.随机轮训.加权轮训),dubbo失败策略(快速失败.失败重试等等), 所以Feign负载均衡策略的是什么? 失败后是否会重试,重试策略又是什么?带这个疑问,查了

  • 解决Spring Cloud Feign 请求时附带请求头的问题

    问题描述 Feign 在请求时是不会将 request 的请求头带着请求的,导致假如 Feign 调用的接口需要请求头的信息,比如当前用户的 token 之类的就获取不到 解决方案 FeignConfiguration 通过实现 Feign 的 RequestInterceptor 将从上下文中获取到的请求头信息循环设置到 Feign 请求头中. /** * feign 配置文件 * 将请求头中的参数,全部作为 feign 请求头参数传递 * @author: linjinp * @create

  • Spring Cloud Feign 使用对象参数的操作

    目录 概述 @RequestBody @SpringQueryMap QueryMapEncoder 解决方案 概述 Spring Cloud Feign 用于微服务的封装,通过接口代理的实现方式让微服务调用变得简单,让微服务的使用上如同本地服务.但是它在传参方面不是很完美.在使用 Feign 代理 GET 请求时,对于简单参数(基本类型.包装器.字符串)的使用上没有困难,但是在使用对象传参时却无法自动的将对象包含的字段解析出来. 如果你没耐心看完,直接跳到最后一个标题跟着操作就行了. @Req

  • 使用Spring Cloud Feign上传文件的示例

    最近经常有人问Spring Cloud Feign如何上传文件.有团队的新成员,也有其他公司的兄弟.本文简单做个总结-- 早期的Spring Cloud中,Feign本身是没有上传文件的能力的(1年之前),要想实现这一点,需要自己去编写Encoder 去实现上传.现在我们幸福了很多.因为Feign官方提供了子项目feign-form ,其中实现了上传所需的 Encoder . 注:笔者测试的版本是Edgware.RELEASE.Camden.Dalston同样适应本文所述. 加依赖 <depen

随机推荐