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ò)誤處理類(lèi)是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("沒(méi)有權(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)求頭就沒(méi)有了
解決辦法使用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)用了同一線(xiàn)程數(shù)據(jù)共享 ThreadLocal
*/
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String cookie = request.getHeader("Cookie");
template.header("Cookie", cookie);
}
};
}
但是上面的辦法只能解決同意線(xiàn)程問(wèn)題,在多線(xiàn)程下還是會(huì)丟失請(qǐng)求頭
多線(xiàn)程下解決辦法:
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
把請(qǐng)求單獨(dú)拿出來(lái)給每個(gè)線(xiàn)程單獨(dú)
RequestContextHolder.setRequestAttributes(requestAttributes);
這樣就可以了~
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式總結(jié)
Linux是集多種功能于一身的操作系統(tǒng),它可以讓用戶(hù)查看和管理當(dāng)下正在運(yùn)行的進(jìn)程,包括Java程序,這篇文章主要給大家總結(jié)介紹了關(guān)于linux系統(tǒng)下java項(xiàng)目在后臺(tái)啟動(dòng)的4種方式,需要的朋友可以參考下2023-10-10
MyBatisPlus 主鍵策略的實(shí)現(xiàn)(4種)
MyBatis Plus 集成了多種主鍵策略,幫助用戶(hù)快速生成主鍵,本文主要介紹了MyBatisPlus主鍵策略的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10
Spring MVC利用Swagger2如何構(gòu)建動(dòng)態(tài)RESTful API詳解
這篇文章主要給大家介紹了關(guān)于在Spring MVC中利用Swagger2如何構(gòu)建動(dòng)態(tài)RESTful API的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
idea中acitviti使用acitBPM插件出現(xiàn)亂碼問(wèn)題及解決方法
這篇文章主要介紹了idea中acitviti使用acitBPM插件出現(xiàn)亂碼問(wèn)題及解決方法,通過(guò)將File Encodings內(nèi)容設(shè)置為UTF-8,本文通過(guò)圖文展示,需要的朋友可以參考下2021-06-06
mybatis-plus生成mapper擴(kuò)展文件的方法
這篇文章主要介紹了mybatis-plus生成mapper擴(kuò)展文件的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
spring cloud 的監(jiān)控turbine-rabbitmq的示例
這篇文章主要介紹了spring cloud 的監(jiān)控turbine-rabbitmq的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05

