博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring Cloud Zuul中异常处理细节
阅读量:6082 次
发布时间:2019-06-20

本文共 5107 字,大约阅读时间需要 17 分钟。

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


本文是Spring Cloud系列的第二十一篇文章,了解前二十篇文章内容有助于更好的理解本文:

1.

2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.


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

图片描述

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

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

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

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

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

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

@Overridepublic 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);}@Overridepublic 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;}

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

@Componentpublic class MyErrorAttribute extends DefaultErrorAttributes {    @Override    public Map
getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace) { Map
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,有问题欢迎留言讨论。

更多JavaEE资料请关注公众号:

图片描述

转载地址:http://khkwa.baihongyu.com/

你可能感兴趣的文章
AFNetworking、MKNetworkKit和ASIHTTPRequest比较
查看>>
Impala 表使用 Avro 文件格式(翻译)
查看>>
http中返回错误代码的意思
查看>>
Spring – 开发环境 – 手动配置
查看>>
!==和!=有什么区别(js php)
查看>>
python入门神图
查看>>
我的友情链接
查看>>
[一文一命令]ls命令详解
查看>>
EBS FORM 失效工具栏按钮
查看>>
com.sun.jdi.InvocationException occurred invoking method
查看>>
Windows Server 2008 R2 IIS7 搭建PHP环境
查看>>
任务计划
查看>>
交换机杂记
查看>>
shell编程总结
查看>>
我的友情链接
查看>>
Java内存与垃圾回收调优
查看>>
Java 8新特性:Stream API
查看>>
swing和java里嵌入浏览器
查看>>
android之SoundPool
查看>>
Python学习-反射相关函数
查看>>