Feign调用报错:failed and no fallback available

timed-out and no fallback

这个错误基本是出现在Hystrix熔断器,熔断器的作用是判断该服务能不能通,如果通了就不管了,调用在指定时间内超时时,就会通过熔断器进行错误返回。

一般设置如下配置的其中一个即可:

1、把时间设长

这里设置5秒

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
2、把超时发生异常属性关闭

hystrix.command.default.execution.timeout.enabled=false
3、禁用feign的hystrix

feign.hystrix.enabled: false
failed and no fallback available:

而通过上面设置只是针对熔断器的错误关闭,并不能解决根本问题,比如Feign客户端调用远程服务时,默认为8秒超时时间,如果在规定时间内没有返回,同样会跳转到熔断器进行处理。即使关闭了熔断器的错误,但是总的错误处理还会是有这个问题出现。

那么要解决根本问题,就要从请求超时时间入手,因为有些服务可能存在调用时间长的问题,所以直接配置:

ribbon.ReadTimeout=60000
ribbon.ConnectTimeout=60000
这些才是真正解决请求超时的问题,如果不设置这个,被调用接口很慢时,会出现Read Timeout on Request。

而针对调用失败重试的次数也可以设置:

ribbon.maxAutoRetries=0

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接口暂时不可用: ";
    @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,就会进入到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 {
    @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,对于HystrixBadRequestException,Feign会直接抛出,不进入熔断方法。

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

参考:http://www.ciphermagic.cn/spring-cloud-feign-hystrix.html

原文地址:https://www.cnblogs.com/chen-chen-chen/p/12202480.html

时间: 2024-11-05 13:04:19

Feign调用报错:failed and no fallback available的相关文章

phprpc远程调用报错,不知道什么原因

我在学习phprpc远程调用,按照网上的教程,发现报错:Incorrect response id (request id: 1, response id: ) n:好纠结啊,求教.贴出代码如下: 服务端,member.php <?php require_once 'jsonRPCServer.php'; /** User: Administrator Date: 2015/12/28 Time: 11:06 */ class member { public function getName()

CXF 客服端调用报错

服务端已经发布了WSDL,现在在客服端生成web service客服端代码,在eclipse中新建一个project,然后new->web services->web service client生产客户端代码 在调用的时候报如下错误 解决:缺少axis相应的jar包,加入包: <dependency> <groupId>axis</groupId> <artifactId>axis</artifactId> <version&

CXF webService 调用报错: “Cannot create a secure XMLInputFactory”

问题描述: 服务端用cxf ---2.7.5 的框架发布了一个服务,将工程打成war包部署到tomcat下运行,用soapui测试,没有任何问题,但是打成ear包部署到weblogic 10.3.6上运行,服务可以正常发布,但是soapui测试调用的时候报一下错误: 原因: 查了一些官网资料,发现这居然是weblogic 10.3.6的一个bug 解决办法: 在weblogic的安装目录可以搜索到weblogic-application.xml,打开该文件可以看到: 注意看上图红色方框内的内容,

artTemplate--使用artTemplate时,由于json对象属性有特殊格式 导致调用报错artTemplate,syntax error,Template Error

我们首先看下面的代码 data = { "siteName" : "西部云谷二期17", "PM10" : "10017", "PM2.5" : 17 }; source = '<li>{{siteName}}</li>' + '<li>{{PM10}}</li>' + '<li>{{[\'PM2.5\']}}</li>'; rende

Hadoop-1,web页面调用报无hbase.jar包【以解决】 2,报java.lang.NoSuchMethodError: org.eclipse.jdt.internal.compiler.CompilationResult.getProblems()[Lorg/eclipse/jdt/core/compiler/IProblem;【以解决】

1:web页面调用报无hbase.jar包 本来java文件就没有问题,但是jsp一调用那个java文件里的方法就报错,报的无hadoop/hbase相关报的问题. 主要解决方法是: 复制hbase/lib里面的jar包到hadoop的lib文件里 然后再打开hadoop/etc/hadoop/hadoop-env.sh 增加这一句: 1 export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:/usr/local/hbase/lib/*:/usr/lib/hbase

fetch 报错 Failed to execute &#39;fetch&#39; on &#39;Window&#39;: Request with GET/HEAD method cannot have body.

TypeError: Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body. 在"窗口"上执行"取"失败:GET / get方法的请求不能有正文. GET requests can't have a request body, you can't make them have one. GET requests only retrieve data,

Android Fragment 嵌套使用报错

在新的SDK每次创建activity时,会自动生成 <pre name="code" class="java">public static class PlaceholderFragment extends Fragment fragment模块,在该模块的基础上进行嵌套fragment代码如下: <pre name="code" class="java">public static class Pla

Vue.js报错Failed to resolve filter问题原因

Vue.js报错Failed to resolve filter问题原因 金刚 vue Vue.js js javascript 之前使用vue.js写分页功能时,写了一个过滤器,发现一个比较奇怪的错误. console控制台调试的时候 提示错误消息: Failed to resolve filter: HomePage console错误信息.jpg 我原来的写法: 原来的错误写法.jpg 错误原因 经过自己的摸索,后来发现竟然是代码顺序错误问题... 由于先执行的pageList,后执行的V

UEditor使用报错Cannot set property &#39;innerHTML&#39; of undefined

仿用UEditor的setContent的时候报错,报错代码如下Uncaught TypeError: Cannot set property 'innerHTML' of undefined.调试ueditor.config.js,ueditor.all.js 提示me.body is undefined.(我就纳了闷儿了!同样的使用那个行这个咋就不行,那个有body,view,这个咋就这么少![我还查看了插件的内部函数,真是个good媛~])错误的原因是没有等UEditor创建完成就使用UE