Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 实践
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请求与解析返回报文都做了包装,如果你的业务场景需要定制一些调用机制,比如:
- 我想在发起请求响应超时失败时自动重试 —— 自定义重试机制
- 我想单独对某些异常的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));
};
}
}
可以看到代码中给了如何获取请求参数和修改请求参数的示例。
总结
小结一下,对于开头提出的场景:
- 我想在发起请求响应超时失败时自动重试 —— 自定义重试机制
- 我想单独对某些异常的HTTP状态码特殊处理 —— 自定义ErrorDecoder
- 服务端接口需要验证签名,所以我方在发起请求时要生成签名然后传过去 —— 定义 Fegin 拦截器
给出了自定义 feign 配置的方式实现的样例代码,希望对你有用,如果有更好的方式简化HTTP请求,欢迎留言分享~
参考链接
Spring Cloud Feign 自定义配置(重试、拦截与错误码处理) 实践的更多相关文章
- 笔记:Spring Cloud Feign Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...
- 笔记:Spring Cloud Feign Hystrix 配置
在 Spring Cloud Feign 中,除了引入了用户客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix,默认情况下,Spring Cl ...
- Spring Cloud Feign Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 的客户端的方式来自定义各个服务客户端调用的参 ...
- 笔记:Spring Cloud Feign 其他配置
请求压缩 Spring Cloud Feign 支持对请求与响应进行GZIP压缩,以减少通信过程中的性能损耗,我们只需要通过下面二个参数设置,就能开启请求与响应的压缩功能,yml配置格式如下: fei ...
- Spring Cloud Feign 声明式服务调用
目录 一.Feign是什么? 二.Feign的快速搭建 三.Feign的几种姿态 参数绑定 继承特性 四.其他配置 Ribbon 配置 Hystrix 配置 一.Feign是什么? 通过对前面Sp ...
- Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,输出异常
Spring Cloud Feign 在调用接口类上,配置熔断 fallback后,出现请求异常时,会进入熔断处理,但是不会抛出异常信息. 经过以下配置,可以抛出异常: 将原有ErrorEncoder ...
- 微服务架构之spring cloud feign
在spring cloud ribbon中我们用RestTemplate实现了服务调用,可以看到我们还是需要配置服务名称,调用的方法 等等,其实spring cloud提供了更优雅的服务调用方式,就是 ...
- Spring cloud Feign 深度学习与应用
简介 Spring Cloud Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单.Feign提供了HTTP请求的模板,通过编写简单的接口和插入注解 ...
- spring coud Feign常用配置
Ribbon配置 在Feign中配置Ribbon非常简单,直接在application.properties中配置即可,如: # 设置连接超时时间 ribbon.ConnectTimeout=500 ...
随机推荐
- PHP zip_entry_open() 函数
定义和用法 zip_entry_open() 函数打开一个 zip 档案以供读取.高佣联盟 www.cgewang.com 如果成功,该函数则返回 TRUE.如果失败,则返回 FALSE. 语法 zi ...
- IC行业常见用语
https://www.cnblogs.com/yeungchie/ Active Devices 有源器件 MOSFET Metal-Oxide-Semicoductor Field-Effect ...
- 2020牛客暑期多校训练营 第二场 C Cover the Tree 构造 贪心
LINK:Cover the Tree 最受挫的是这道题,以为很简单 当时什么都想不清楚. 先胡了一个树的直径乱搞的贪心 一直过不去.后来意识到这类似于最经典长链剖分优化贪心的做法 然后那个是求最大值 ...
- P4491 [HAOI2018]染色 广义容斥 NTT 生成函数
LINK:染色 算是比较常规的广义容斥. 算恰好k个 可以直接转成至少k个. 至少k个非常的好求 直接生成函数. 设\(g_k\)表示至少有k个颜色是满足的 那么有 \(g_k=C(m,k)\frac ...
- 数据结构C语言实现----选择排序
选择排序 第一步:从一串无序数字串中选一个最小的与第一个数交换位置 第二步:从剩下的数字中选一个最小的与第二个数交换位置 第三步:从剩下的数字中选一个最小的与第三个数字交换位置 以此类推... 运行结 ...
- Ubuntu环境下使用Jupyter Notebook查找桌面.csv文档的方法
这个问题困扰了我很久,最后在一个老师发来的完成结果里找到了答案.(奇怪的是教材里没有.老师也不讲.尤其是百度也没有啊啊啊啊) 好了进入正题.教材里的原话是这样的 这行代码实现的环境应该是在window ...
- 【Python 实例】面向对象 | 按逗号分割列表
[Python 实例]面向对象 | 按逗号分割列表 题目: 按逗号分割列表 应该得到如下结果: ["xx"],["xx"],["xx"] 解 ...
- python4.3内置函数
常见的内置函数 a=[12,31,31,232,34,32,43,54,36]max1=max(a)#最大函数print(max1)min1=min(a)#最小函数print(min1)sum1=su ...
- QWebEngineView简单使用
QWebEngineView是提供一个访问web页面的widget,这里是一个简单的使用代码 头文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H #includ ...
- 003_对go语言中的工作池代码练习的一些思考和改进
在进行工作池的代码练习时候,我发现了一个有趣的事情,首先看下面一段代码: package main import "fmt" import "time" fun ...