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

 更新时间:2021年06月23日 15:49:57   作者:萌中芢  
这篇文章主要介绍了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);

这样就可以了~

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

相关文章

  • Spring中数据访问对象Data Access Object的介绍

    Spring中数据访问对象Data Access Object的介绍

    今天小编就为大家分享一篇关于Spring中数据访问对象Data Access Object的介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • java中的定时器和多线程

    java中的定时器和多线程

    这篇文章主要介绍了java中的定时器和多线程用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • SpringBoot整合mybatis-generator-maven-plugin的方法

    SpringBoot整合mybatis-generator-maven-plugin的方法

    这篇文章主要介绍了SpringBoot整合mybatis-generator-maven-plugin,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • shiro之INI配置详解

    shiro之INI配置详解

    这篇文章主要为大家详细介绍了shiro之INI配置的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • SpringBoot前后端分离项目之打包、部署到服务器详细图文流程

    SpringBoot前后端分离项目之打包、部署到服务器详细图文流程

    作为后台开发,项目打包部署是经常性的操作,下面这篇文章主要给大家介绍了关于SpringBoot前后端分离项目之打包、部署到服务器的相关资料,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-12-12
  • Java 替换word文档文字并指定位置插入图片

    Java 替换word文档文字并指定位置插入图片

    这篇文章主要介绍了Java 替换word文档文字,指定位置插入图片功能,本文通过实例代码给大家讲解,需要的朋友可以参考下
    2018-02-02
  • 详解Guava中EventBus的使用

    详解Guava中EventBus的使用

    EventBus是Guava的事件处理机制,是设计模式中观察者模式(生产/消费者编程模型)的优雅实现。本文就来和大家聊聊EventBus的使用,需要的可以参考一下
    2022-12-12
  • 详解path和classpath的区别

    详解path和classpath的区别

    这篇文章主要介绍了详解path和classpath的区别的相关资料,需要的朋友可以参考下
    2017-06-06
  • ActiveMQ持久化机制代码实例

    ActiveMQ持久化机制代码实例

    这篇文章主要介绍了ActiveMQ持久化机制代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • IntelliJ IDEA(2020.2)的下载、安装步骤详细教程

    IntelliJ IDEA(2020.2)的下载、安装步骤详细教程

    这篇文章主要介绍了IntelliJ IDEA(2020.2)的下载、安装步骤详细教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08

最新评论