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

人在魔都,目前为贝壳做事,本篇文章主要分享下 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请求与解析返回报文都做了包装,如果你的业务场景需要定制一些调用机制,比如:

  1. 我想在发起请求响应超时失败时自动重试 —— 自定义重试机制
  2. 我想单独对某些异常的HTTP状态码特殊处理 —— 自定义ErrorDecoder
  3. 服务端接口需要验证签名,所以我方在发起请求时要生成签名然后传过去 —— 定义 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 Feign 自定义配置(重试、拦截与错误码处理) 实践的更多相关文章

  1. 笔记:Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  2. 笔记:Spring Cloud Feign Hystrix 配置

    在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cl ...

  3. Spring Cloud Feign Ribbon 配置

    由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...

  4. 笔记:Spring Cloud Feign 其他配置

    请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...

  5. Spring Cloud Feign 声明式服务调用

    目录 一.Feign是什么? 二.Feign的快速搭建 三.Feign的几种姿态 参数绑定 继承特性 四.其他配置 Ribbon 配置 Hystrix 配置 一.Feign是什么? ​ 通过对前面Sp ...

  6. Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,输出异常

    Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,出现请求异常时,会进入熔断处理,但是不会抛出异常信息. 经过以下配置,可以抛出异常: 将原有ErrorEncoder ...

  7. 微服务架构之spring cloud feign

    在spring cloud ribbon中我们用RestTemplate实现了服务调用,可以看到我们还是需要配置服务名称,调用的方法 等等,其实spring cloud提供了更优雅的服务调用方式,就是 ...

  8. Spring cloud Feign 深度学习与应用

    简介 Spring Cloud Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单.Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解 ...

  9. spring coud Feign常用配置

    Ribbon配置 在Feign中配置Ribbon非常简单,直接在application.properties中配置即可,如: # 设置连接超时时间 ribbon.ConnectTimeout=500 ...

随机推荐

  1. Spring学习之动态代理的简单实现

    先说一下代理模式的好处 1.可以使真实角色的操作更加纯粹,不用去关注一些公共的业务 2.公共的交给代理角色,实现了业务的分工 3.公共业务发生扩展的时候,方便集中管理 静态代理模式的缺点 1.一个真实 ...

  2. 一款功能简约到可怜的SQL 客户端

    你有一个思想,我有一个思想,我们交换后,一个人就有两个思想 If you can NOT explain it simply, you do NOT understand it well enough ...

  3. windows:shellcode 代码远程APC注入和加载

    https://www.cnblogs.com/theseventhson/p/13197776.html  上一章介绍了通用的shellcode加载器,这个加载器自己调用virtualAlloc分配 ...

  4. zabbix脚本发送邮件告警

    目录 zabbix邮箱告警 内部使用第三方邮箱发送邮箱告警 zabbix使用第三方邮箱发送告警 通过脚本使用第三方邮箱发送邮箱告警 zabbix邮箱告警 环境说明: zabbix服务端 192.168 ...

  5. Tarjan 做题总结

    这两天Tarjan复习完后把题做了做.洛谷题单<图的连通性>已经做得差不多了.大部分是Tarjan的题,所以写一篇小总结. T1 [模板] 缩点 不多bb.我已经写过关于Tarjan模板的 ...

  6. firewalld 极速上手指南

    从CentOS6迁移到7系列,变化有点多,其中防火墙就从iptables变成了默认Firewalld服务.firewalld网上资料很多,但没有说得太明白的.一番摸索后,总结了这篇文章,用于快速上手. ...

  7. 041_go语言中的panic

    代码演示: package main import "os" func main() { // panic("a problem") _, err := os. ...

  8. Android Studio同时监听多个Button实现activity跳转

    MainActivity.java: package com.example.test; import android.content.Intent; import android.os.Bundle ...

  9. Python数据类型-dic,set常见操作

    字典常见方法   语法:字典名[新key]=value 功能:给字典增加键值 语法:字典名[字典里存在的key]=新的value 功能:修改字典里的值   功能:删除字典的元素,通过key来进行删除, ...

  10. Spring学习之——手写Spring源码V2.0(实现IOC、D、MVC、AOP)

    前言 在上一篇<Spring学习之——手写Spring源码(V1.0)>中,我实现了一个Mini版本的Spring框架,在这几天,博主又看了不少关于Spring源码解析的视频,受益匪浅,也 ...