前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud Zuul中异常处理细节

Spring Cloud Zuul中异常处理细节

作者头像
江南一点雨
发布2018-04-02 15:49:41
3K0
发布2018-04-02 15:49:41
举报
文章被收录于专栏:玩转JavaEE玩转JavaEE

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方法,这是整个调用过程的核心,如下:

代码语言:javascript
复制
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方法的一部分核心代码,如下:

代码语言:javascript
复制
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对象中提取出来,核心代码如下:

代码语言:javascript
复制
@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;
}

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

代码语言:javascript
复制
@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,有问题欢迎留言讨论。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2017-10-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江南一点雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档