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 != nullthrow 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,有问题欢迎留言讨论。

springcloud Zuul中异常处理细节的更多相关文章

  1. springcloud Zuul中路由配置细节

    上篇文章我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节. 首先 ...

  2. SpringCloud学习之Zuul统一异常处理及回退

    一.Filter中统一异常处理 其实在SpringCloud的Edgware SR2版本中对于ZuulFilter中的错误有统一的处理,但是在实际开发当中对于错误的响应方式,我想每个团队都有自己的处理 ...

  3. SpringCloud+ZUUL跨域请求中的OPTIONS请求处理

    目前项目结构是VUE做前端,后端采用微服务架构,在开发时前端需要跨域请求数据,通过CorsConfig配置解决了简单跨域请求需要.但当需要在请求的header中增加token信息时,出现了请求失败的情 ...

  4. Spring cloud Zuul网关异常处理

    Spring cloud Zuul网关异常处理 一 异常测试: 1> 创建一个pre类型的过滤器,并在该过滤器的run方法实现中抛出一个异常.比如下面的实现,在run方法中调用的doSometh ...

  5. SpringCloud Zuul 路由映射规则配置

    阅读目录 前言 快速入门 路由详解 Cookie与头信息 本地跳转 Hystrix和Ribbon支持 过滤器解释 动态加载 后记 回到目录 前言 本文起笔于2018-06-26周二,接了一个这周要完成 ...

  6. springcloud Zuul学习笔记

    SpringCloud Zull是一个基于NetflixZuul实现的API网关组件,它实现了请求路由,负载均衡,校验过滤等功能;本文主要记录springcloud zuul的入门级demo开发过程; ...

  7. 服务网关zuul之三:zuul统一异常处理

    我们详细介绍了Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不同作用.我们会发现在这些核心过滤器中并没有实现error阶段的过滤器.那么这些过滤器可以用 ...

  8. Nacos(四):SpringCloud项目中接入Nacos作为配置中心

    前言 通过前两篇文章: Nacos(二):Nacos与OpenFeign的对接使用 Nacos(三):SpringCloud项目中接入Nacos作为注册中心 相信大家已经对Nacos作为注册中心的基本 ...

  9. spring-cloud zuul网关

    API Gateway 是随着微服务(Microservice)这个概念一起兴起的一种架构模式,它用于解决微服务过于分散,没有一个统一的出入口进行流量管理的问题. 使用 Zuul 实现 API Gat ...

随机推荐

  1. LDAP的前世今生

    上世界80年代,就有了LDAP的雏形. 我接触到最早的Windows系列的服务器,是Windows2000 Professional版本里可以加入ActiveDirectory,后来从Windows2 ...

  2. PHP中Cookie的使用

    1.什么是Cookie? Cookie保存在客户端浏览器中,cookie是Http头的一部分,通过浏览器请求页面时,它会被通过Http头的形式发送过去.被请求的页面,可以通过PHP来获取cookie的 ...

  3. Windows 2012r2 以及以上版本远程提示错误的解决方法

    部分机器远程时会提示如图: 其实解决问题非常简单 .... 为了防止不会操作 完整的截图展示. 服务器下面进行处理 打开我的电脑 属性 打开远程设置 将框中的选项取消掉 然后就可以了.

  4. [转帖]devops 容器管理平台 rancher 简介

    https://testerhome.com/topics/10828 chenhengjie123 for PPmoney · 2017年11月13日 · 最后由 c19950809 回复于 201 ...

  5. Orchard是如何运行的

    建立一个CMS网站(内容管理系统)是不同于建立一个普通的web站点:它更像是建立一个应用程序容器. 设计这样一个系统时,必须建立一流的可扩展性功能.这必需是一个非常开放式的构架,但是一个开放性的系统可 ...

  6. robotframework常用的几个快捷键

    robotframework常用的几个快捷键 重命名(F2) 搜索关键字(F5) 执行用例(F8) 创建新工程(ctrl+n) 创建新测试套(ctrl+shift+f) 创建新用例(ctrl+shif ...

  7. 【bzoj4596】[Shoi2016]黑暗前的幻想乡 容斥原理+矩阵树定理

    题目描述 给出 $n$ 个点和 $n-1$ 种颜色,每种颜色有若干条边.求这张图多少棵每种颜色的边都出现过的生成树,答案对 $10^9+7$ 取模. 输入 第一行包含一个正整数 N(N<=17) ...

  8. Java之Map的使用场景

    总结之 Map接口 的使用场景(day04) Map: Map中的集合,元素是成对存在的(理解为夫妻).每个元素由键与值两部分组成,通过键可以找对所对应的值 Map中的集合不能包含重复的键,值可以重复 ...

  9. win10用vncviewer远程登陆ubuntu桌面

    一:安装Ubuntu的服务端桌面环境 # 安装xrdpsudo apt-get install xrdp # 安装xfce4sudo apt-get updatesudo apt-get instal ...

  10. MT【184】$\epsilon$助力必要性

    已知满足不等式$|x^2-4x+a|+|x-3|\le5$的最大值为$3$,求实数$a$的值,并解该不等式. 证明:1)当$x=3$时,$|a-3|\le5$,得$a\in[-2,8]$2)$\for ...