Feign調(diào)用接口解決處理內(nèi)部異常的問(wèn)題
問(wèn)題描述:
當(dāng)使用feign調(diào)用接口,出現(xiàn)400~500~的接口問(wèn)題時(shí)。會(huì)出錯(cuò)feign:FeignException。(因?yàn)槭清e(cuò)誤,只能用catch Throwable,不可使用catch Exception捕獲異常)導(dǎo)致程序無(wú)法繼續(xù)運(yùn)行。
問(wèn)題原因:
由于feign默認(rèn)的錯(cuò)誤處理類是FunFeignFallback會(huì)throw new AfsBaseExceptio導(dǎo)致外部無(wú)法捕獲異常。
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調(diào)用失敗", cause); return IResponse.fail("請(qǐng)求失敗,請(qǐng)稍后再試"); } 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("授權(quán)失敗==========原始返回信息:[{}]",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("接口調(diào)用失敗"); } if(method.getReturnType().equals(IResponse.class)){ if(exception instanceof FeignException.Forbidden){ return IResponse.fail("沒有權(quán)限").setCode("403"); } if(exception instanceof FeignException.NotFound){ return IResponse.fail("請(qǐng)求路徑不存在").setCode("404"); } if(exception instanceof FeignException.BadRequest){ return IResponse.fail("參數(shù)錯(cuò)誤").setCode("400"); } if(exception.content()==null||exception.content().length==0){ return IResponse.fail("請(qǐng)求失敗,請(qǐng)稍后再試"); } 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("接口調(diào)用失敗"); } } @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); } }
問(wèn)題解決:自定義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調(diào)用 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: 發(fā)票驗(yàn)證接口 * @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("獲取業(yè)務(wù)數(shù)據(jù)API接口") @PostMapping(value = "/vi/check") @AfsFeignClear(true)//通過(guò)此注解防止添加內(nèi)部token JSONObject InvoiceCheck(@RequestBody InvoiceCheckDto dto, @RequestHeader Map<String, String> headers); }
3.實(shí)現(xiàn)自定義報(bào)錯(cuò)處理
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 接口調(diào)用失敗", cause); return null; } }
Feign遠(yuǎn)程調(diào)用失敗-----丟請(qǐng)求頭
@FeignClient("guli-cart") public interface CartFenignService { @GetMapping("/currentUserCartItems") List<OrderItemVo> getCurrentUserCartItems(); }// 這樣去掉接口時(shí)其實(shí)Feign在底層是一個(gè)全新的requst所有請(qǐng)求頭就沒有了
解決辦法使用Feign遠(yuǎn)程掉用攔截器,在遠(yuǎn)程請(qǐng)求是先創(chuàng)建攔截器
@Bean("requestInterceptor") public RequestInterceptor requestInterceptor() { return new RequestInterceptor() { @Override public void apply(RequestTemplate template) { /** * 把以前的Cookie放到新請(qǐng)求中去 原理就是運(yùn)用了同一線程數(shù)據(jù)共享 ThreadLocal */ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String cookie = request.getHeader("Cookie"); template.header("Cookie", cookie); } }; }
但是上面的辦法只能解決同意線程問(wèn)題,在多線程下還是會(huì)丟失請(qǐng)求頭
多線程下解決辦法:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
把請(qǐng)求單獨(dú)拿出來(lái)給每個(gè)線程單獨(dú)
RequestContextHolder.setRequestAttributes(requestAttributes);
這樣就可以了~
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java StringUtils字符串分割轉(zhuǎn)數(shù)組的實(shí)現(xiàn)
這篇文章主要介紹了Java StringUtils字符串分割轉(zhuǎn)數(shù)組的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Java設(shè)計(jì)模式編程中的工廠方法模式和抽象工廠模式
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的工廠方法模式和抽象工廠模式,設(shè)計(jì)模式的建立有利于團(tuán)隊(duì)協(xié)作時(shí)代碼的共同維護(hù),需要的朋友可以參考下2016-01-01SpringBoot調(diào)用Poi-tl實(shí)現(xiàn)渲染數(shù)據(jù)并生成Word文檔
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何調(diào)用Poi-tl實(shí)現(xiàn)渲染數(shù)據(jù)并生成Word文檔,文中的示例代碼講解詳細(xì),有需要的小伙伴可以了解下2023-09-09Java多線程中的Exchanger應(yīng)用簡(jiǎn)析
這篇文章主要介紹了Java多線程中的Exchanger應(yīng)用簡(jiǎn)析,Exchanger提供了一個(gè)同步點(diǎn)exchange方法,兩個(gè)線程調(diào)用exchange方法時(shí),無(wú)論調(diào)用時(shí)間先后,兩個(gè)線程會(huì)互相等到線程到達(dá)exchange方法調(diào)用點(diǎn),此時(shí)兩個(gè)線程可以交換數(shù)據(jù),將本線程產(chǎn)出數(shù)據(jù)傳遞給對(duì)方,需要的朋友可以參考下2023-12-12Java線程的start方法回調(diào)run方法的操作技巧
面試過(guò)程中經(jīng)常會(huì)被面試官問(wèn)到為什么我們調(diào)用start()方法時(shí)會(huì)執(zhí)行run()方法,為什么不能直接調(diào)用run()方法,問(wèn)的一頭霧水,今天小編給大家介紹下Java線程的start方法回調(diào)run方法的操作技巧,需要的朋友參考下吧2017-11-11Springboot下RedisTemplate的兩種序列化方式實(shí)例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過(guò)定義一個(gè)配置類,自定義RedisTemplate的序列化方式,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09Java調(diào)用WebService接口作測(cè)試
這篇文章主要介紹了Java調(diào)用WebService接口作測(cè)試,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11LinkedList學(xué)習(xí)示例模擬堆棧與隊(duì)列數(shù)據(jù)結(jié)構(gòu)
這篇文章主要介紹了LinkedList學(xué)習(xí)示例,模擬一個(gè)堆棧與隊(duì)列數(shù)據(jù)結(jié)構(gòu),大家參考使用吧2014-01-01淺談Java自定義類加載器及JVM自帶的類加載器之間的交互關(guān)系
這篇文章主要介紹了淺談Java自定義類加載器及JVM自帶的類加載器之間的交互關(guān)系,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Java?GUI實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java?GUI實(shí)現(xiàn)學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01