Spring Cloud Feign 熔断机制填坑
原文链接:www.ciphermagic.cn/spring-clou…
问题
最近在项目开发中,使用 Feign 调用服务,当触发熔断机制时,遇到了以下问题:
- 异常信息形如:
TestService#addRecord(ParamVO) failed and no fallback available.
; - 获取不到服务提供方抛出的原始异常信息;
- 实现某些业务方法不进入熔断,直接往外抛出异常;
接下来将一一解决上述问题。
对于failed and no fallback available.
这种异常信息,是因为项目开启了熔断:
feign.hystrix.enabled: true
复制代码
当调用服务时抛出了异常,却没有定义fallback
方法,就会抛出上述异常。由此引出了第一个解决方式。
@FeignClient
加上fallback
方法,并获取异常信息
为@FeignClient
修饰的接口加上fallback
方法有两种方式,由于要获取异常信息,所以使用fallbackFactory
的方式:
@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class)
public interface TestService {@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)Result get(@PathVariable("id") Integer id);}
复制代码
在@FeignClient
注解中指定fallbackFactory
,上面例子中是TestServiceFallback
:
import feign.hystrix.FallbackFactory;
import org.apache.commons.lang3.StringUtils;@Component
public class TestServiceFallback implements FallbackFactory<TestService> {private static final Logger LOG = LoggerFactory.getLogger(TestServiceFallback.class);public static final String ERR_MSG = "Test接口暂时不可用: ";@Overridepublic TestService create(Throwable throwable) {String msg = throwable == null ? "" : throwable.getMessage();if (!StringUtils.isEmpty(msg)) {LOG.error(msg);}return new TestService() {@Overridepublic String get(Integer id) {return ResultBuilder.unsuccess(ERR_MSG + msg);}};}
}
复制代码
通过实现FallbackFactory
,可以在create
方法中获取到服务抛出的异常。但是请注意,这里的异常是被Feign
封装过的异常,不能直接在异常信息中看出原始方法抛出的异常。这时得到的异常信息形如:
status 500 reading TestService#addRecord(ParamVO); content:
{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}
复制代码
说明一下,本例子中,服务提供者的接口返回信息会统一封装在自定义类Result
中,内容就是上述的content
:
{"success":false,"resultCode":null,"message":"/ by zero","model":null,"models":[],"pageInfo":null,"timelineInfo":null,"extra":null,"validationMessages":null,"valid":false}
复制代码
因此,异常信息我希望是message
的内容:/ by zero
,这样打日志时能够方便识别异常。
保留原始异常信息
当调用服务时,如果服务返回的状态码不是200,就会进入到Feign
的ErrorDecoder
中,因此如果我们要解析异常信息,就要重写ErrorDecoder
:
import feign.Response;
import feign.Util;
import feign.codec.ErrorDecoder;/*** @Author: CipherCui* @Description: 保留 feign 服务异常信息* @Date: Created in 1:29 2018/6/2*/
public class KeepErrMsgConfiguration {@Beanpublic ErrorDecoder errorDecoder() {return new UserErrorDecoder();}/*** 自定义错误解码器*/public class UserErrorDecoder implements ErrorDecoder {private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic Exception decode(String methodKey, Response response) {Exception exception = null;try {// 获取原始的返回内容String json = Util.toString(response.body().asReader());exception = new RuntimeException(json);// 将返回内容反序列化为Result,这里应根据自身项目作修改Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);// 业务异常抛出简单的 RuntimeException,保留原来错误信息if (!result.isSuccess()) {exception = new RuntimeException(result.getMessage());}} catch (IOException ex) {logger.error(ex.getMessage(), ex);}return exception;}}}
复制代码
上面是一个例子,原理是根据response.body()
反序列化为自定义的Result
类,提取出里面的message
信息,然后抛出RuntimeException
,这样当进入到熔断方法中时,获取到的异常就是我们处理过的RuntimeException
。
注意上面的例子并不是通用的,但原理是相通的,大家要结合自身的项目作相应的修改。
要使上面代码发挥作用,还需要在@FeignClient
注解中指定configuration
:
@FeignClient(name = "serviceId", fallbackFactory = TestServiceFallback.class, configuration = {KeepErrMsgConfiguration.class})
public interface TestService {@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)String get(@PathVariable("id") Integer id);}
复制代码
不进入熔断,直接抛出异常
有时我们并不希望方法进入熔断逻辑,只是把异常原样往外抛。这种情况我们只需要捉住两个点:不进入熔断、原样。
原样就是获取原始的异常,上面已经介绍过了,而不进入熔断,需要把异常封装成HystrixBadRequestException
,对于HystrixBadRequestException
,Feign
会直接抛出,不进入熔断方法。
因此我们只需要在上述KeepErrMsgConfiguration
的基础上作一点修改即可:
/*** @Author: CipherCui* @Description: feign 服务异常不进入熔断* @Date: Created in 1:29 2018/6/2*/
public class NotBreakerConfiguration {@Beanpublic ErrorDecoder errorDecoder() {return new UserErrorDecoder();}/*** 自定义错误解码器*/public class UserErrorDecoder implements ErrorDecoder {private Logger logger = LoggerFactory.getLogger(getClass());@Overridepublic Exception decode(String methodKey, Response response) {Exception exception = null;try {String json = Util.toString(response.body().asReader());exception = new RuntimeException(json);Result result = JsonMapper.nonEmptyMapper().fromJson(json, Result.class);// 业务异常包装成 HystrixBadRequestException,不进入熔断逻辑if (!result.isSuccess()) {exception = new HystrixBadRequestException(result.getMessage());}} catch (IOException ex) {logger.error(ex.getMessage(), ex);}return exception;}}}
复制代码
总结
为了更好的达到熔断效果,我们应该为每个接口指定fallback
方法。而根据自身的业务特点,可以灵活的配置上述的KeepErrMsgConfiguration
和NotBreakerConfiguration
,或自己编写Configuration
。
以上例子特殊性较强,不足之处请不吝指教。希望大家可以从中获取到有用的东西,应用到自己的项目中,感谢阅读。
原文链接:www.ciphermagic.cn/spring-clou…
Spring Cloud Feign 熔断机制填坑相关推荐
- hystrix 页面_微服务 | 使用Hystrix实现Spring Cloud的熔断机制
1. 熔断机制介绍 在介绍熔断机制之前,我们需要了解微服务的雪崩效应.在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进.但是,一个应用可能会有多个微服 ...
- Spring Cloud Hystrix熔断机制原理剖析
一.前言 在分布式系统架构中多个系统之间通常是通过远程RPC调用进行通信,也就是 A 系统调用 B 系统服务,B 系统调用 C 系统的服务.当尾部应用 C 发生故障而系统 B 没有服务降级时候可能会导 ...
- Spring Cloud Feign使用详解
通过前面两章对Spring Cloud Ribbon和Spring Cloud Hystrix的介绍,我们已经掌握了开发微服务应用时,两个重要武器,学会了如何在微服务架构中实现客户端负载均衡的服务调 ...
- Spring Cloud Feign原理
Spring Cloud Feign原理 Feign运行过程 重试机制 服务降级 负载均衡 隔舱原理 Feign运行过程 通过主类上的EnableFeignClients 注解开启FeignClien ...
- Spring Cloud Feign 请求时附带请求头
Spring Cloud Feign 请求时附带请求头 问题描述 解决方案 FeignConfiguration 使用 配置修改 问题描述 Feign 在请求时是不会将 request 的请求头带着请 ...
- Spring Cloud的负载均衡Spring Cloud Ribbon和Spring Cloud Feign
一.客户端负载均衡:Spring Cloud Ribbon. Spring Cloud Ribbon是基于HTTP和TCP的客户端负载工具,它是基于Netflix Ribbon实现的.通过Spring ...
- Spring Cloud Feign传输Header,并保证多线程情况下也适用
Spring Cloud Feign传输Header,并保证多线程情况下也适用 一.现象 微服务在生产中,常遇到需要把 header 传递到下一子服务的情况(如服务A访问服务B的接口,需要传递head ...
- Spring Cloud Feign调用令牌携带问题
Spring Cloud Feign调用令牌携带问题 微服务项目中模块之间的调用,检测令牌的合法性问题不可避免.使用feign拦截器可以解决. 1:在公用模块中添加maven依赖 [外链图片转存失败, ...
- Spring Cloud Feign原理详解
目录 1.什么是Feign? 2.Open Feign vs Spring Cloud Feign 2.1.OpenFeign 2.2.Spring Cloud Open Feign 3.Spring ...
最新文章
- 物联网成网络安全防护新重点!
- 国产搜索引擎首超Google 中文搜索瓦解霸权?
- android 上传到了maven,但是报错找不到jar
- 如何在Google Chrome浏览器中启动JavaScript调试器?
- mongodb 只查询某个字段
- java的4种代码块
- iOS之instancetype
- java 判断是否包含中文_Java 判断字符串是否包含中文正则表达式
- 实现一个符合标准的Promise
- Ubuntu 16.04下用Wine运行的软件出现方块的解决思路(应该是兼容现在所有平台的Wine碰到这个的问题)
- 虚拟机VMware的Ubuntu下安装tensorflow详解
- PHP 抽象工厂模式(Kit模式)
- 关于期权的若干硬知识,知道这些就不那么担心了
- 六大写作软件功能解说,网络作家不可错过的码字软件宝典
- linux-----基本操作指令(2)
- python 绘制正弦余弦函数 matplotlib的基本使用
- web缓存—Squid代理服务
- 面部识别法案正式通过?微软总裁赞不绝口——华盛顿州重大突破!
- 面试后说hold什么意思_面试快结束时,如果面试官对你说这几句话,说明你被淘汰了!...
- 线程池6th卷:大展经纶补天手