SpringCloud feign服务熔断下的异常处理操作

网友投稿 771 2023-01-02

SpringCloud feign服务熔断下的异常处理操作

SpringCloud feign服务熔断下的异常处理操作

今天做项目的时候,遇到一个问题,如果我调用某个服务的接口,但是这个服务挂了,同时业务要求这个接口的结果是必须的,那我该怎么办呢,答案是通过hystrix,但是又有一点,服务不是平白无故挂的(排除服务器停电等问题),也就是说有可能是timeout or wrong argument 等等,那么我该如何越过hystrix的同时又能将异常成功抛出呢

第一点:先总结一下异常处理的方式:

1):通过在controller中编写@ExceptionHandler 方法 ,直接在controller中编写异常处理器方法

@RequestMAPPing("/test")

public ModelAndView test()

{

throw new TmallBaseException();

}

@ExceptionHandler(TmallBaseException.class)

public ModelAndView handleBaseException()

{

return new ModelAndView("error");

}

但是呢这种方法只能在这个controller中有效,如果其他的controller也抛出了这个异常,是不会执行的

2):全局异常处理:

@ControllerAdvice

public class AdminExceptionHandler

{

@ExceptionHandler(TmallBaseException.class)

public ModelAndView hAndView(Exception exception)

{

//logic

return null;

}

}

本质是aop代理,如名字所言,全局异常处理,可以处理任意方法抛出的异常

3)通过实现SpringMVC的HandlerExceptionResolver接口

public static class Tt implements HandlerExceptionResolver

{

@Override

public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,

Exception ex)

{

//logic

return null;

}

}

然后在mvc配置中添加即可

@Configuration

public class MyConfiguration extends WebMvcConfigurerAdapter {

@Override

public void configureHandlerExceptionResolvers(List exceptionResolvers) {

//初始化异常处理器链

exceptionResolvers.add(new Tt());

}

}

接下来就是Fegin ,如果想自定义异常需要了解1个接口:ErrorDecoder

先来看下rmi调用结束后是如果进行decode的

Object executeAndDecode(RequestTemplate template) throws Throwable {

Request request = targetRequest(template);

//代码省略

try {

if (logLevel != Logger.Level.NONE) {

response =

logger.logAndRebufferResponse(metadata.configKey(), logLevel, response, elapsedTime);

response.toBuilder().request(request).build();

}

if (Response.class == metadata.returnType()) {

if (response.body() == null) {

return response;

}

if (response.body().length() == null ||

response.body().length() > MAX_RESPONSE_BUFFER_SIZE) {

shouldClose = false;

return response;

}

// Ensure the response body is disconnected

byte[] bodyData = Util.toByteArray(response.body().asInputStream());

return response.toBuilder().body(bodyData).build();

}

//从此处可以发现,如果状态码不再200-300,或是404的时候,意味着非正常响应就会对内部异常进行解析

if (response.status() >= 200 && response.status() < 300) {

if (void.class == metadata.returnType()) jvjGsz{

return null;

} else {

return decode(response);

}

} else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {

return decode(response);

} else {

throw errorDecoder.decode(metadata.configKey(), response);

}

} catch (IOException e) {

if (logLevel != Logger.Level.NONE) {

logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime);

}

throw errorReading(request, response, e);

} finally {

if (shouldClose) {

ensureClosed(response.body());

}

}

}

默认的解析方式是:

public static class Default implements ErrorDecoder {

private final RetryAfterDecoder retryAfterDecoder = new RetryAfterDecoder();

@Override

public Exception decode(String methodKey, Response response) {

http:// //获取错误状态码,生成fegin自定义的exception

FeignException exception = errorStatus(methodKey, response);

Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));

if (retryAfter != null) {

//如果重试多次失败,则抛出相应的exception

return new RetryableException(exception.getMessage(), exception, retryAfter);

}

//否则抛出默认的exception

return exception;

}

我们可以发现,做了2件事,第一获取状态码,第二重新抛出异常,额外的判断是否存在多次失败依然error的异常,并没有封装太多的异常,既然如此那我们就可以封装我们自定义的异常了

但是注意,这块并没有涉及hystrix,也就意味着对异常进行处理还是会触发熔断机制,具体避免方法最后讲

首先我们编写一个BaseException 用于扩展:省略getter/setter

public class TmallBaseException extends RuntimeException

{

/**

*

* @author joker

* @date 创建时间:2018年8月18日 下午4:46:54

*/

private static final long serialVersionUID = -5076254306303975358L;

// 未认证

public static final int UNAUTHENTICATED_EXCEPTION = 0;

// 未授权

public static final int FORBIDDEN_EXCEPTION = 1;

// 超时

public static final int TIMEOUT_EXCEPTION = 2;

// 业务逻辑异常

public static final int BIZ_EXCEPTION = 3;

// 未知异常->系统异常

public static final int UNKNOWN_EXCEPTION = 4;

// 异常码

private int code;

// 异常信息

private String message;

public TmallBaseException(int code, String message)

{

super(message);

this.code = code;

this.message = message;

}

public TmallBaseException(String message, Throwable cause)

{

super(message, cause);

this.message = message;

}

public TmallBaseException(int code, String message, Throwable cause)

{

super(message, cause);

this.code = code;

this.message = message;

}

}

OK,我们定义好了基类之后可以先进行测试一番:服务接口controller:

//显示某个商家合作的店铺

@RequestMapping(value="/store")

public ResultDTO>findStoreOperatedBrands(@RequestParam("storeId")Long storeId)

{

为了测试,先直接抛出异常

throw new TmallBaseException(TmallBaseException.BIZ_EXCEPTION, "ceshi");

}

接口:

@RequestMapping(value="/auth/brand/store",method=RequestMethod.POST,produces=MediaType.APPLICATION_jsON_UTF8_VALUE)

ResultDTO>findStoreOperatedBrands(@RequestParam("storeId")Long storeId);

其余的先不贴了,然后我们发起rest调用的时候发现,抛出异常之后并没有被异常处理器处理,这是因为我们是通过fegin,而我又配置了feign的fallback类,抛出异常的时候会自动调用这个类中的方法.

有两种解决方法:

1.直接撤除hystrix ,很明显its not a good idea

2.再封装一层异常类,具体为何,如下

AbstractCommand#handleFallback 函数是处理异常的函数,从方法后缀名可以得知,当exception 是HystrixBadRequestException的时候是直接抛出的,不会触发fallback,也就意味着不会触发降级

final Func1> handleFallback = new Func1>() {

@Override

public Observable call(Throwable t) {

circuitBreaker.markNonSuccess();

Exception e = getExceptionFromThrowable(t);

executionResult = executionResult.setExecutionException(e);

if (e instanceof RejectedExecutionException) {

return handleThreadPoolRejectionViaFallback(e);

} else if (t instanceof HystrixTimeoutException) {

return handleTimeoutViaFallback();

} else if (t instanceof HystrixBadRequestException) {

return handleBadRequestByEmittingError(e);

} else {

/*

* Treat HystrixBadRequestException from ExecutionHook like a plain HystrixBadRequestException.

*/

if (e instanceof HystrixBadRequestException) {

eventNotifier.markEvent(HystrixEventType.BAD_REQUEST, commandKey);

return Observable.error(e);

}

return handleFailureViaFallback(e);

}

}

};

既然如此,那一切都明了了,修改类的继承结构即可:

public class TmallBaseException extends HystrixBadRequestException

{

/**

*

* @author joker

* @date 创建时间:2018年8月18日 下午4:46:54

*/

private static final long serialVersionUID = -5076254306303975358L;

// 未认证

public static final int UNAUTHENTICATED_EXCEPTION = 0;

// 未授权

public static final int FORBIDDEN_EXCEPTION = 1;

// 超时

public static final int TIMEOUT_EXCEPTION = 2;

// 业务逻辑异常

public static final int BIZ_EXCEPTION = 3;

// 未知异常->系统异常

public static final int UNKNOWN_EXCEPTION = 4;

// 异常码

private int code;

// 异常信息

private String message;

}

至于怎么从服务器中获取异常然后进行转换,就是通过上面所讲的ErrorHandler:

public class TmallErrorDecoder implements ErrorDecoder

{

@Override

public Exception decode(String methodKey, Response response)

{

System.out.println(methodKey);

Exception exception=null;

try

{

String json = Util.toString(response.body().asReader());

exception=JsonUtils.json2Object(json,TmallBaseException.class);

} catch (IOException e)

{

e.printStackTrace();

}

return exception!=null?exception:new TmallBaseException(TmallBaseException.UNKNOWN_EXCEPTION, "系统运行异常");

}

}

最后微服务下的全局异常处理就ok了,当然这个ErrorDdecoder 和BaseException推荐放在common模块下,所有其它模块都会使用到它。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:一网通办个人不动产(一网通办个人不动产登记证明)
下一篇:玄武区企业app开发平台(玄武区门户网站)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~