欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-25 10:00:43
问题描述:当使用feign调用接口,出现400~500~的接口问题时。会出错feign:feignexception。(因为是错误,只能用catch throwable,不可使用catch excep...

问题描述:

当使用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);

这样就可以了~

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