问题描述:

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

这样就可以了~

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