springcloud Zuul中异常处理细节

Spring Cloud Zuul对异常的处理整体来说还是比较方便的,流程也比较清晰,只是由于Spring Cloud发展较快,各个版本之间有差异,导致有的小伙伴在寻找这方面的资料的时候经常云里雾里,本文将以Dalston.SR3版本为例,来说明Spring Cloud Zuul中的异常处理问题。

首先我们来看一张官方给出的Zuul请求的生命周期图,如下:

关于这张图我说如下几点:

  1. 正常情况下所有的请求都是按照pre、route、post的顺序来执行,然后由post返回response

  2. 在pre阶段,如果有自定义的过滤器则执行自定义的过滤器

  3. pre、routing、post的任意一个阶段如果抛异常了,则执行error过滤器,然后再执行post给出响应

这是这张图给我们的信息,我们再来看看源码com.netflix.zuul.http.ZuulServlet类中的service方法,这是整个调用过程的核心,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
try  {
     init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
     // Marks this request as having passed through the "Zuul engine", as opposed to servlets
     // explicitly bound in web.xml, for which requests will not have the same data attached
     RequestContext context = RequestContext.getCurrentContext();
     context.setZuulEngineRan();
     try  {
         preRoute();
     catch  (ZuulException e) {
         error(e);
         postRoute();
         return ;
     }
     try  {
         route();
     catch  (ZuulException e) {
         error(e);
         postRoute();
         return ;
     }
     try  {
         postRoute();
     catch  (ZuulException e) {
         error(e);
         return ;
     }
catch  (Throwable e) {
     error( new  ZuulException(e,  500 "UNHANDLED_EXCEPTION_"  + e.getClass().getName()));
finally  {
     RequestContext.getCurrentContext().unset();
}

我们看到这里有一个大的try…catch,大的try…catch里边有三个小的try…catch,小的try…catch只负责捕获ZuulException异常,其他的异常交给大的try…catch来捕获。pre和route执行出错之后都会先执行error再执行post,而post执行出错之后就只执行error而不会再执行post。

ZuulFilter最终会在com.netflix.zuul.FilterProcessor的processZuulFilter方法中被调用,在该方法中会判断runFilter是否执行成功,如果执行失败,则将异常信息提取出来,然后抛出异常,抛出的异常如果是ZuulException的实例,则抛出一个ZuulException类型的异常,如果不是ZuulException的实例,则抛出一个状态码为500的ZuulException类型的异常,所以无论如何,我们最终看到的都是ZuulException类型的异常,下面我贴出processZuulFilter方法的一部分核心代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  Object processZuulFilter(ZuulFilter filter)  throws  ZuulException {
     try  {
         ZuulFilterResult result = filter.runFilter();
         ExecutionStatus s = result.getStatus();
         execTime = System.currentTimeMillis() - ltime;
         switch  (s) {
             case  FAILED:
                 t = result.getException();
                 ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                 break ;
             case  SUCCESS:
                 break ;
             default :
                 break ;
         }
         if  (t !=  null throw  t;
         usageNotifier.notify(filter, s);
         return  o;
     catch  (Throwable e) {
         usageNotifier.notify(filter, ExecutionStatus.FAILED);
         if  (e  instanceof  ZuulException) {
             throw  (ZuulException) e;
         else  {
             ZuulException ex =  new  ZuulException(e,  "Filter threw Exception" 500
             filter.filterType() +  ":"  + filterName);
             ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
             throw  ex;
         }
     }
}

在Zuul中,所有的错误问题的最终都是被SendErrorFilter类来处理,该类在早期的版本是一个post类型的filter,post类型的filter有一个缺陷就是不能处理post中抛出的异常,需要我们手动去完善,而我目前使用的这个版本(Dalston.SR3)已经修复了这个问题,SendErrorFilter现在是一个error类型的filter,而且只要RequestContext中有异常就会进入到SendErrorFilter中,错误信息也都从exception对象中提取出来,核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
public  boolean  shouldFilter() {
     RequestContext ctx = RequestContext.getCurrentContext();
     // only forward to errorPath if it hasn't been forwarded to already
     return  ctx.getThrowable() !=  null
             && !ctx.getBoolean(SEND_ERROR_FILTER_RAN,  false );
}
@Override
public  Object run() {
     try  {
         RequestContext ctx = RequestContext.getCurrentContext();
         ZuulException exception = findZuulException(ctx.getThrowable());
         HttpServletRequest request = ctx.getRequest();
         request.setAttribute( "javax.servlet.error.status_code" , exception.nStatusCode);
         log.warn( "Error during filtering" , exception);
         request.setAttribute( "javax.servlet.error.exception" , exception);
         if  (StringUtils.hasText(exception.errorCause)) {
             request.setAttribute( "javax.servlet.error.message" , exception.errorCause);
         }
         RequestDispatcher dispatcher = request.getRequestDispatcher(
                 this .errorPath);
         if  (dispatcher !=  null ) {
             ctx.set(SEND_ERROR_FILTER_RAN,  true );
             if  (!ctx.getResponse().isCommitted()) {
                 dispatcher.forward(request, ctx.getResponse());
             }
         }
     }
     catch  (Exception ex) {
         ReflectionUtils.rethrowRuntimeException(ex);
     }
     return  null ;
}

如果我们想要自定义异常信息也很方便,如下:

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public  class  MyErrorAttribute  extends  DefaultErrorAttributes {
     @Override
     public  Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,  boolean  includeStackTrace) {
         Map<String, Object> result =  super .getErrorAttributes(requestAttributes, includeStackTrace);
         result.put( "status" 222 );
         result.put( "error" "error" );
         result.put( "exception" "exception" );
         result.put( "message" "message" );
         return  result;
     }
}

好了,关于Spring Cloud Zuul中异常处理我们就说这么多,笔者之前有一篇文章介绍了Spring Boot中的异常处理,想深入了解异常处理的小伙伴可以查看一下那篇文章,OK,有问题欢迎留言讨论。

转载于:https://www.cnblogs.com/xyhero/p/f8b6a987325c004f8e0ec3b7d9e2c2fe.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值