坑一:服务端使用全局拦截异常统一处理,客户端未能捕获到异常
服务端中,使用了全局拦截异常统一处理(@RestControllerAdvice),返回统一格式信息,导致feign客户端调用服务端时候,当服务端出现异常,客户端捕获不到异常,也未能进入熔断,降级
@Slf4j
@RestControllerAdvice
public class ExceptionHandle {
@ExceptionHandler(Exception.class)
private Result handle(Exception exception) {
return null;
}
}
解决办法:@RestControllerAdvice 注解中指定 需要统一拦截异常包 ,feign 调用 包路径不包括在内
@Slf4j
@RestControllerAdvice({"com.test.controller"})
//@RestControllerAdvice
public class ExceptionHandle {
}
坑二:feign开启熔断后,获取不到服务提供方抛出的原始异常信息
异常信息例如:
WxServiceApi#getTokenByAppId(String) failed and no fallback available.;
预期目标:获取原始异常信息返回
解决办法一:关闭熔断
feign:
hystrix:
enabled: false
解决办法二:实现ErrorDecoder ,使用自定义ErrorDecoder
ErrorDecoder 可以全局,也可以在 @FeignClient 中指定
全局:不进入熔断
@Slf4j
@Configuration
public class ExceptionErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String var1, Response response) {
try {
if (response.body() != null) {
String body = Util.toString(response.body().asReader());
log.error(body);
ExceptionInfo exceptionInfo = JSON.parseObject(body, new TypeReference<ExceptionInfo>() {
});
// Class clazz = Class.forName(exceptionInfo.getException());
//Exception exception = (Exception) clazz.getDeclaredConstructor(String.class).newInstance(exceptionInfo.getMessage());
return new HystrixBadRequestException(exceptionInfo.getMessage());
//return exception;
}
} catch (Exception var4) {
log.error(var4.getMessage());
return new InternalException(var4.getMessage());
}
return new InternalException("system error");
}
}
ExceptionInfo 类
@Data
public class ExceptionInfo {
private Long timestamp;
private Integer status;
private String exception;
private String message;
private String path;
private String error;
}
单个@FeignClient 中指定
进入熔断:
public class KeepErrMsgConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new UserErrorDecoder();
}
/**
* 自定义错误
*/
public class UserErrorDecoder implements ErrorDecoder {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Exception decode(String methodKey, Response response) {
Exception exception = null;
try {
// 获取原始的返回内容
String json = Util.toString(response.body().asReader());
exception = new RuntimeException(json);
// 将返回内容反序列化为Result,这里应根据自身项目作修改
Result result = JSONObject.parseObject(json, Result.class);
// 业务异常抛出简单的 RuntimeException,保留原来错误信息
if (!ResultUtil.isSuccess(result)) {
exception = new RuntimeException(result.getObj().toString());
}
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
return exception;
}
}
}
不进入熔断:
public class NotBreakerConfiguration {
@Bean
public ErrorDecoder errorDecoder() {
return new UserErrorDecoder();
}
/**
* 自定义错误
*/
public class UserErrorDecoder implements ErrorDecoder {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public Exception decode(String methodKey, Response response) {
Exception exception = null;
try {
String json = Util.toString(response.body().asReader());
exception = new RuntimeException(json);
// 将返回内容反序列化为Result,这里应根据自身项目作修改
Result result = JSONObject.parseObject(json, Result.class);
// 业务异常抛出简单的 RuntimeException,保留原来错误信息
if (!ResultUtil.isSuccess(result)) {
exception = new HystrixBadRequestException(result.getObj().toString());
}
} catch (IOException ex) {
logger.error(ex.getMessage(), ex);
}
return exception;
}
}
}
@FeignClient 注解配置
@FeignClient(name = "wx-service",url ="http://localhost:8082", fallbackFactory = WxHystrixClientFallbackFactory.class, configuration = NotBreakerConfiguration.class)
@Component
interface WxServiceApi {
}
根据自身项目,来实现所需要的errorDecoder配置,灵活使用
参考:
https://blog.csdn.net/hnhygkx/article/details/89492031