原文链接: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,就会进入到FeignErrorDecoder中,因此如果我们要解析异常信息,就要重写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对于HystrixBadRequestExceptionFeign会直接抛出,不进入熔断方法。

因此我们只需要在上述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方法。而根据自身的业务特点,可以灵活的配置上述的KeepErrMsgConfigurationNotBreakerConfiguration,或自己编写Configuration

以上例子特殊性较强,不足之处请不吝指教。希望大家可以从中获取到有用的东西,应用到自己的项目中,感谢阅读。

原文链接:www.ciphermagic.cn/spring-clou…

Spring Cloud Feign 熔断机制填坑相关推荐

  1. hystrix 页面_微服务 | 使用Hystrix实现Spring Cloud的熔断机制

    1. 熔断机制介绍 在介绍熔断机制之前,我们需要了解微服务的雪崩效应.在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进.但是,一个应用可能会有多个微服 ...

  2. Spring Cloud Hystrix熔断机制原理剖析

    一.前言 在分布式系统架构中多个系统之间通常是通过远程RPC调用进行通信,也就是 A 系统调用 B 系统服务,B 系统调用 C 系统的服务.当尾部应用 C 发生故障而系统 B 没有服务降级时候可能会导 ...

  3. Spring Cloud Feign使用详解

     通过前面两章对Spring Cloud Ribbon和Spring Cloud Hystrix的介绍,我们已经掌握了开发微服务应用时,两个重要武器,学会了如何在微服务架构中实现客户端负载均衡的服务调 ...

  4. Spring Cloud Feign原理

    Spring Cloud Feign原理 Feign运行过程 重试机制 服务降级 负载均衡 隔舱原理 Feign运行过程 通过主类上的EnableFeignClients 注解开启FeignClien ...

  5. Spring Cloud Feign 请求时附带请求头

    Spring Cloud Feign 请求时附带请求头 问题描述 解决方案 FeignConfiguration 使用 配置修改 问题描述 Feign 在请求时是不会将 request 的请求头带着请 ...

  6. Spring Cloud的负载均衡Spring Cloud Ribbon和Spring Cloud Feign

    一.客户端负载均衡:Spring Cloud Ribbon. Spring Cloud Ribbon是基于HTTP和TCP的客户端负载工具,它是基于Netflix Ribbon实现的.通过Spring ...

  7. Spring Cloud Feign传输Header,并保证多线程情况下也适用

    Spring Cloud Feign传输Header,并保证多线程情况下也适用 一.现象 微服务在生产中,常遇到需要把 header 传递到下一子服务的情况(如服务A访问服务B的接口,需要传递head ...

  8. Spring Cloud Feign调用令牌携带问题

    Spring Cloud Feign调用令牌携带问题 微服务项目中模块之间的调用,检测令牌的合法性问题不可避免.使用feign拦截器可以解决. 1:在公用模块中添加maven依赖 [外链图片转存失败, ...

  9. Spring Cloud Feign原理详解

    目录 1.什么是Feign? 2.Open Feign vs Spring Cloud Feign 2.1.OpenFeign 2.2.Spring Cloud Open Feign 3.Spring ...

最新文章

  1. 物联网成网络安全防护新重点!
  2. 国产搜索引擎首超Google 中文搜索瓦解霸权?
  3. android 上传到了maven,但是报错找不到jar
  4. 如何在Google Chrome浏览器中启动JavaScript调试器?
  5. mongodb 只查询某个字段
  6. java的4种代码块
  7. iOS之instancetype
  8. java 判断是否包含中文_Java 判断字符串是否包含中文正则表达式
  9. 实现一个符合标准的Promise
  10. Ubuntu 16.04下用Wine运行的软件出现方块的解决思路(应该是兼容现在所有平台的Wine碰到这个的问题)
  11. 虚拟机VMware的Ubuntu下安装tensorflow详解
  12. PHP 抽象工厂模式(Kit模式)
  13. 关于期权的若干硬知识,知道这些就不那么担心了
  14. 六大写作软件功能解说,网络作家不可错过的码字软件宝典
  15. linux-----基本操作指令(2)
  16. python 绘制正弦余弦函数 matplotlib的基本使用
  17. web缓存—Squid代理服务
  18. 面部识别法案正式通过?微软总裁赞不绝口——华盛顿州重大突破!
  19. 面试后说hold什么意思_面试快结束时,如果面试官对你说这几句话,说明你被淘汰了!...
  20. 线程池6th卷:大展经纶补天手

热门文章

  1. 研究人员吐槽当前AI训练效率过于低下
  2. AI 落地,数据安全绕不开的 4 大问题
  3. 2019需要关注的几大AI趋势
  4. 干货丨卷积神经网络工作原理的直观解释
  5. 收藏版超全机器学习资料合集
  6. 2021年诺贝尔经济学奖评述:解决重大社会问题的自然实验因果框架
  7. 因果推断研究获2021诺贝尔经济学奖
  8. 谷歌用量子计算机造出「时间晶体」,挑战热力学第二定律
  9. Hinton构思下一代神经网络:属于无监督对比学习
  10. 史上曾被认为不可能的十大科学难题全被实现