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@Slf4jpublic 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; @Componentpublic 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@Componentpublic 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);

这样就可以了~

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

别让别人徘徊的脚步踩碎你明天美好的梦想,

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

相关文章:

你感兴趣的文章:

标签云: