赞
踩
Spring Cloud Alibaba 用Feign调取服务时,如果服务端发生异常要传递给客户端,或者客户端拦截到异常响应做相应的业务处理,可有几种实现。此例子源码请看GitHub
我使用的熔断组件是sentinel,其他的熔断组件一样。Feign都是支持的;
feign:
sentinel:
enabled: true
@Component
@FeignClient(name = ServiceConstants.FEIGN_SERVICE,fallback = FeignServiceFallback.class,configuration = FeignConfig.class)
public interface FeignService {
@GetMapping(value = ServiceConstants.FEIGN_SERVICE_API_TEST)
TestObject getTestObject();
}
@Slf4j
@Component
public class FeignServiceFallback implements FeignService {
@Override
public TestObject getTestObject() {
log.error("服务调用接口异常");
return null;
}
}
FeignServiceFallback 类就是服务不可用时的返回策略。当然还有一个fallbackFactory 后边会说。
这有个问题,只有服务不可用时才会发生这个熔断,异常是不走这的。
public class FeignException implements ErrorDecoder {
@Override
public Exception decode(String s, Response response) {
System.out.println("访问结果码:"+response.status());
return null;
}
}
配置类实例化;@FeignClient(configuration = FeignConfig.class) 上边有
@Component
public class FeignConfig {
@Bean
public ErrorDecoder errorDecoder(){
return new FeignException();
}
}
实现这个接口只能获取到非200状态的异常返回。正常请求是获取不到的。比如在服务端返回自定义status码的异常,这获取不到。只能获取到4xx、5xx。(http 接口项目,一般都会自定义返回结构和状态码。Http的状态码还是200。如果需要传递业务异常那需要另外的办法。)
Feign定义的异常有几种基本都是继承的FeignException,Feign因为使用HttpClient或okHttp 调用服务接口,在Spring Mvc 中直接用全局异常catch不到。(谁有办法告知一下)所以只能在返回数据处做一下处理。
Feign提供了一个ResponseEntityDecoder类,对数据做编码处理,可以用这个类拦截一下返回数据。
public class FeignConfig {
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
public Decoder feignDecoder(){
return new AwardResponseEntityDecoder(new SpringDecoder(this.messageConverters));
}
}
public class AwardResponseEntityDecoder extends ResponseEntityDecoder { public AwardResponseEntityDecoder(Decoder decoder) { super(decoder); } @Override public Object decode(Response response, Type type) throws IOException,FeignException { if (response.body() == null) { throw new DecodeException(response.status(), "返回数据为空", response.request()); } String bodyStr = Util.toString(response.body().asReader(Util.UTF_8)); //对结果进行转换 ResultBody resultBody=JSON.parseObject(bodyStr,ResultBody.class); //此处如果不是正常的200状态,则抛出异常,异常就会进入熔断,走熔断策略 if (resultBody.getCode() != 200) { throw new DecodeException(resultBody.code, resultBody.message, response.request()); } return jsonToobj(bodyStr,type); } /** * 反序列化 * @param jsonStr * @param targetType * @param <T> * @return */ public static <T> T jsonToobj(String jsonStr, Type targetType) { try { JavaType javaType = TypeFactory.defaultInstance().constructType(targetType); ObjectMapper mapper = new ObjectMapper(); return mapper.readValue(jsonStr, javaType); } catch (IOException e) { throw new IllegalArgumentException("Feign JSON转换为对象异常:" + jsonStr, e); } } }
服务端发生异常有统一返回包装类,如下;如果没有异常则返回的是一个正常的对象(本例为TestObject )。所以在上边会先转化为ResultBody 对象,如果code有值则发生了异常。
public class ResultBody implements Serializable { public boolean success; public int code; public String message; public Object data; public Long timestamp = System.currentTimeMillis(); ///省略..... }
这样根据返回内容抛出DecodeException会直接进入熔断处理器。二中的 FeignServiceFallback
我的客户端也统一返回ResultBody所以返回结果是这样的:
{
"success": true,
"code": 200,
"message": "",
"data": null,
"timestamp": 1600155600278
}
data中的null就是FeignServiceFallback返回的内容。可以返回对应对象。
进一步处理业务异常问题,当前只是知道了服务端发生了异常,并不知道具体发生了什么。我想直接返回服务端异常内容。
把(三) 的内容改造一下即可直接返回服务端异常。
fallback 改为 fallbackFactory
@Component
@FeignClient(name = ServiceConstants.FEIGN_SERVICE,fallbackFactory = GeneralFallbackFactory.class,configuration = FeignConfig.class)
public interface FeignService {
@GetMapping(value = ServiceConstants.FEIGN_SERVICE_API_TEST)
TestObject getTestObject();
}
/**
* FeignClient 设置fallbackFactory 返回统一格式异常 与fallback不能共用
* ServiceException 是自定义的异常类,ErrorCode.SERVICE_ERROR是自定义错误码,请自行创建。
*/
@Component
public class GeneralFallbackFactory implements FallbackFactory {
@Override
public Object create(Throwable throwable) {
throw new ServiceException(throwable.getMessage(), ErrorCode.SERVICE_ERROR);
}
}
全局异常处理异常ServiceException
@ExceptionHandler(value = ServiceException.class)
public ResultBody handleServiceException(ServiceException se) {
logger.error("ServiceException:{}", se.getMessage());
return new ResultBody(se.getErrorCode().code, se.getMessage());
}
FallbackFactory 也是熔断处理器,其中可以获取到异常信息,所以使用FallbackFactory 客户端可以看到异常信息,抛出异常捕获。当然FallbackFactory还有其他用法请根据需求自行处理。
现在客户端调用获得的结果可以直接显示服务端异常:
{
"success": false,
"code": 2000,
"message": "未登录",
"data": null,
"timestamp": 1600156631671
}
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。