博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Cloud Feign 熔断机制填坑
阅读量:6331 次
发布时间:2019-06-22

本文共 5359 字,大约阅读时间需要 17 分钟。

原文链接:

问题

最近在项目开发中,使用 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;@Componentpublic class TestServiceFallback implements FallbackFactory
{ private static final Logger LOG = LoggerFactory.getLogger(TestServiceFallback.class); public static final String ERR_MSG = "Test接口暂时不可用: "; @Override public TestService create(Throwable throwable) { String msg = throwable == null ? "" : throwable.getMessage(); if (!StringUtils.isEmpty(msg)) { LOG.error(msg); } return new TestService() { @Override public 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 {    @Bean    public ErrorDecoder errorDecoder() {        return new UserErrorDecoder();    }    /**     * 自定义错误解码器     */    public class UserErrorDecoder implements ErrorDecoder {        private Logger logger = LoggerFactory.getLogger(getClass());        @Override        public 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 {    @Bean    public ErrorDecoder errorDecoder() {        return new UserErrorDecoder();    }    /**     * 自定义错误解码器     */    public class UserErrorDecoder implements ErrorDecoder {        private Logger logger = LoggerFactory.getLogger(getClass());        @Override        public 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

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

原文链接:

转载地址:http://jkboa.baihongyu.com/

你可能感兴趣的文章
Java 程序在执行时的内存划分区域
查看>>
关于c#动态加载程序集的一些注意事项
查看>>
对Apache2进行简单配置
查看>>
Extensions for Vue
查看>>
java-多线程 String 常量池缓存功能带来的问题
查看>>
8.5折!图表控件TeeChart特价中...
查看>>
大专生自学Python到找到工作的经历
查看>>
大白话讲解闭包笔试题
查看>>
好用的API文档--在线版
查看>>
Linux 性能測试工具
查看>>
剑指offer——优化时间和空间效率(第五章)
查看>>
门面(Facade)模式--医院,保安系统实例
查看>>
linux(Ubuntu/Centos) iproute 路由IP地址等命令集合,查看端口链接
查看>>
php 常用的JS
查看>>
text-overflow
查看>>
python之路之面向对象3
查看>>
codeforces 940D 比赛总结
查看>>
ulua
查看>>
面向对象与面向过程区别
查看>>
D3js-实现图形拖拽及双击恢复
查看>>