Feign调用接口解决处理内部异常的问题

问题描述:

当使用feign调用接口,出现400~500~的接口问题时。会出错feign:FeignException。(因为是错误,只能用catch Throwable,不可使用catch Exception捕获异常)导致程序无法继续运行。

问题原因:

由于feign默认的错误处理类是FunFeignFallback会throw new AfsBaseExceptio导致外部无法捕获异常。

package com.ruicar.afs.cloud.common.core.feign;
import com.alibaba.fastjson.JSONObject;
import com.ruicar.afs.cloud.common.core.exception.AfsBaseException;
import com.ruicar.afs.cloud.common.core.util.IResponse;
import feign.FeignException;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.lang.Nullable;
import java.lang.reflect.Method;
import java.util.Objects;

@Data
@AllArgsConstructor
@Slf4j
public class FunFeignFallback<T> implements MethodInterceptor {
    private final Class<T> targetType;
    private final String targetName;
    private final Throwable cause;
    private static byte JSON_START = '{';

    @Nullable
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String errorMessage = cause.getMessage();
        if (!(cause instanceof FeignException)) {
            log.error("FunFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage);
            log.error("feign调用失败", cause);
            return IResponse.fail("请求失败,请稍后再试");
        }
        int status = ((FeignException.FeignClientException) this.cause).status();
        boolean isAuthFail = (status==426||status==403||status==401)&&"afs-auth".equals(targetName);
        FeignException exception = (FeignException) cause;
        if(isAuthFail){
            log.warn("授权失败==========原始返回信息:[{}]",exception.contentUTF8());
        }else {
            log.error("FunFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage);
            log.error("", cause);
            log.error("原始返回信息{}",exception.contentUTF8());
        }
        if(method.getReturnType().equals(Void.class)){
            throw new AfsBaseException("接口调用失败");
        }
        if(method.getReturnType().equals(IResponse.class)){
            if(exception instanceof FeignException.Forbidden){
                return IResponse.fail("没有权限").setCode("403");
            }
            if(exception instanceof FeignException.NotFound){
                return IResponse.fail("请求路径不存在").setCode("404");
            }
            if(exception instanceof FeignException.BadRequest){
                return IResponse.fail("参数错误").setCode("400");
            }

            if(exception.content()==null||exception.content().length==0){
                return IResponse.fail("请求失败,请稍后再试");
            }
            if(JSON_START==exception.content()[0]){
                return JSONObject.parseObject(exception.content(),IResponse.class);
            }else{
                return IResponse.fail(exception.contentUTF8());
            }
        }else{
            try {
                if(method.getReturnType().equals(String.class)){
                    return exception.contentUTF8();
                }else if(method.getReturnType().equals(JSONObject.class)){
                    if(JSON_START==exception.content()[0]){
                        return JSONObject.parseObject(exception.content(), JSONObject.class);
                    }
                }else if(!method.getReturnType().equals(Object.class)){
                    return JSONObject.parseObject(exception.content(), method.getReturnType());
                }
                if(JSON_START==exception.content()[0]){
                    JSONObject jsonObject = JSONObject.parseObject(exception.content(), JSONObject.class);
                    if(jsonObject.containsKey("code")&&jsonObject.containsKey("msg")) {
                        return jsonObject.toJavaObject(IResponse.class);
                    }
                }
            }catch (Throwable e){}
            throw new AfsBaseException("接口调用失败");
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FunFeignFallback<?> that = (FunFeignFallback<?>) o;
        return targetType.equals(that.targetType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(targetType);
    }
}

问题解决:自定义feignFallback异常处理:

1.自定义异常处理 InvoiceApiFeignFallbackFactory

package com.ruicar.afs.cloud.invoice.factory;
import com.ruicar.afs.cloud.invoice.fallback.InvoiceApiFeignFallback;
import com.ruicar.afs.cloud.invoice.feign.InvoiceApiFeign;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;

@Component
public class InvoiceApiFeignFallbackFactory implements FallbackFactory<InvoiceApiFeign> {
    @Override
    public InvoiceApiFeign create(Throwable throwable) {
        InvoiceApiFeignFallback invoiceApiFeignFallback = new InvoiceApiFeignFallback();
        invoiceApiFeignFallback.setCause(throwable);
        return invoiceApiFeignFallback;
    }
}

2.feign调用 InvoiceApiFeignFallbackFactory

package com.ruicar.afs.cloud.invoice.feign;
import com.alibaba.fastjson.JSONObject;
import com.ruicar.afs.cloud.common.core.feign.annotations.AfsFeignClear;
import com.ruicar.afs.cloud.invoice.dto.InvoiceCheckDto;
import com.ruicar.afs.cloud.invoice.factory.InvoiceApiFeignFallbackFactory;
import io.swagger.annotations.ApiOperation;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.Map;

/**
 * @description: 发票验证接口
 * @author: rongji.zhang
 * @date: 2020/8/14 10:32
 */
@FeignClient(name = "invoice", url = "${com.greatwall.systems.invoice-system.url}" ,fallbackFactory = InvoiceApiFeignFallbackFactory.class)
public interface InvoiceApiFeign {
    /**
     *
     * @param dto
     * @return
     */
    @ApiOperation("获取业务数据API接口")
    @PostMapping(value = "/vi/check")
    @AfsFeignClear(true)//通过此注解防止添加内部token
    JSONObject InvoiceCheck(@RequestBody InvoiceCheckDto dto, @RequestHeader Map<String, String> headers);
}

3.实现自定义报错处理

package com.ruicar.afs.cloud.invoice.fallback;
import com.alibaba.fastjson.JSONObject;
import com.ruicar.afs.cloud.invoice.dto.InvoiceCheckDto;
import com.ruicar.afs.cloud.invoice.feign.InvoiceApiFeign;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;

/**
 * @author Fzero
 * @date 2019-01-24
 */
@Slf4j
@Component
public class InvoiceApiFeignFallback implements InvoiceApiFeign {
    @Setter
    private Throwable cause;
    /**
     * @param dto
     * @param headers
     * @return
     */
    @Override
    public JSONObject InvoiceCheck(InvoiceCheckDto dto, Map<String, String> headers) {
        log.error("feign 接口调用失败", cause);
        return null;
    }
}

Feign远程调用失败-----丢请求头

@FeignClient("guli-cart")
public interface CartFenignService {
    @GetMapping("/currentUserCartItems")
    List<OrderItemVo> getCurrentUserCartItems();
}// 这样去掉接口时其实Feign在底层是一个全新的requst所有请求头就没有了

解决办法使用Feign远程掉用拦截器,在远程请求是先创建拦截器

@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
    return new RequestInterceptor() {
        @Override
        public void apply(RequestTemplate template) {
            /**
             * 把以前的Cookie放到新请求中去   原理就是运用了同一线程数据共享   ThreadLocal
             */
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            String cookie = request.getHeader("Cookie");
            template.header("Cookie", cookie);
        }
    };
}

但是上面的办法只能解决同意线程问题,在多线程下还是会丢失请求头

多线程下解决办法:

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

把请求单独拿出来给每个线程单独

RequestContextHolder.setRequestAttributes(requestAttributes);

这样就可以了~

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringBoot如何使用feign实现远程接口调用和错误熔断

    这篇文章主要介绍了SpringBoot如何使用feign实现远程接口调用和错误熔断,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.第一步,新建两个简单的springboot项目并创建rest接口 demo系统的rest接口 plus系统的调用接口 2.在项目pom文件里导入feign和hystrix的pom依赖包 <properties> <java.version>1.8</java.version> <s

  • Spring Cloud中关于Feign的常见问题总结

    一.FeignClient接口,不能使用@GettingMapping 之类的组合注解 代码示例: @FeignClient("microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "/simple/{id}", method = RequestMethod.GET) public User findById(@PathVariable(&quo

  • 解决feign调用接口不稳定的问题

    我就废话不多说了,大家还是直接看代码吧~ Caused by: java.net.SocketException: Software caused connection abort: recv failed at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketIn

  • feign服务端发现异常客户端处理的方法介绍

    前言 在使用feign进行远程方法调用时,如果远程服务端方法出现异常,客户端有时需要捕获,并且把异常信息返回给前端,而如果在开启熔断之后,这个异常会被消化,所以说,如果希望拿到服务端异常,feign.hystrix.enable需要设置为false,而当不开熔断时,我们也有几种方法把拿到服务端的异常信息,下面总结一下. try...catch方法 feign异常拦截器 try...catch方法 这个方法比较直观,也最好理解,但业务层会有很多try...catch这种代码,所以不是很推荐. 注意

  • Feign调用接口解决处理内部异常的问题

    问题描述: 当使用feign调用接口,出现400-500-的接口问题时.会出错feign:FeignException.(因为是错误,只能用catch Throwable,不可使用catch Exception捕获异常)导致程序无法继续运行. 问题原因: 由于feign默认的错误处理类是FunFeignFallback会throw new AfsBaseExceptio导致外部无法捕获异常. package com.ruicar.afs.cloud.common.core.feign; impo

  • 使用feign调用接口时调不到get方法的问题及解决

    目录 feign调用接口调不到get方法 feign调用拿不到数据 feign调用接口调不到get方法 记录今天在使用springcloud的feign调用接口时踩的坑. 调用的方法是get方法时调用不到接口的问题 1.feign调用时默认的请求方式是post请求,所以如果是要调用的请求为get请求,并且有参数传递时的解决方法: (1)在调用的接口上需要给参数添加@RequestParam注解 @RequestMapping(value = "/cust-archives", meth

  • 如何自定义feign调用实现hystrix超时、异常熔断

    需求描述 spring cloud 项目中feign 整合 hystrix经常使用,但是最近发现hystrix功能强大,但是对我们来说有些大材小用. 首先我只需要他的一个熔断作用,就是说请求超时.异常了返回 FeignClient注解中配置的fallback,不需要非阻塞操作.也不需要重试,hystrix 调用feign时候做了线程池隔离处理,这样增加了项目复杂度(线程池参数配置.线程少了请求服务直接拒绝,多了线程得管理...) 目前feign 超时之后是直接抛异常的,这样的话虽然是及时熔断了,

  • 微服务之间如何通过feign调用接口上传文件

    具体需求: 我们的项目是基于springboot框架的springcloud微服务搭建的,后端服务技术层面整体上分为business服务和core服务,business服务用于作为应用层,直接连接客户端,通常用于聚合数据,core服务用来客户端具体操作不同需求来控制数据库,文件上传是通过客户端上传接口,通过business服务,由服务端调用feign接口,也是第一次做这种文件中转,遇到各种问题,下面是我自己的解决方案,不喜勿喷,代码小白一枚; 一.core服务层接口@requestmapping

  • 使用Feign设置Token鉴权调用接口

    目录 Feign设置Token鉴权调用接口 声明FeignClient指定url 调用测试 返回对象可以封装demo 先去implementsRequestInterceptor重写apply方法 配置拦截器 Feign调用进行Token鉴权 项目场景 解决办法 具体实现 注意有Bug!!! Feign设置Token鉴权调用接口 声明FeignClient 指定url /**  * CREATE BY songzhongjin ON 2021.05.08 15:58 星期六  * DESC:fe

  • 解决微服务feign调用添加token的问题

    微服务feign调用添加token 1.一般情况是这么配置的 具体的怎么调用就不说了 如下配置,就可以在请求头中添加需要的请求头信息. package localdate; import feign.RequestInterceptor; import feign.RequestTemplate; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; imp

  • SpringBoot使用Feign调用其他服务接口

    使用SpringCloud的Feign组件能够为服务间的调用节省编码时间并提高开发效率,当服务本身不复杂时可以单独将该组件拿出使用. 引入依赖 <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign --> <dependency> <groupId>org.springframework.cloud</groupId>

  • 解决SpringAop内部调用时不经过代理类的问题

    目录 SpringAop AOP代理织入时期 动态代理方式 解决SpringAop内部调用时不经过代理类(而是通过this) 方案一 方案二 this使得SpringAop失效之谜 问题描述 问题剖析 SpringAop AOP代理织入时期 编译时织入 - aspectj框架 类加载时织入 - aspectj框架 运行时织入 - spring-aop 动态代理方式 JDK - 被代理对象必须需要实现接口 CGLIB - 采用继承被代理对象方式实现代理功能 解决SpringAop内部调用时不经过代

  • 解决Feign调用的GET参数传递的问题

    目录 需求 思考 GET方式请求① GET方式请求② GET方式请求③ 需求 ​ 在消费方服务通过GET方式,访问服务提供方的接口,需要传递多参数,拆分成多个参数的方式访问,不太适合用在该场景,需要改造成合适的方式调用服务方的接口 思考 拆分成多个参数时,若GET请求的参数超过3个及以上时,便不适用该种方式请求服务,因为这样传递参数过于臃肿,可读性也比较差: 若改造成POST请求的方式,虽然解决参数过多的问题,但是也带来了其他的开销,参数被放到了body里面,然后请求到服务方提供的接口,服务方的

随机推荐