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. java词频统计——web版支持

    需求概要: 1.把程序迁移到web平台,通过用户上传TXT的方式接收文件. 2.用户直接输入要统计的文本,服务器返回结果 3.在页面上给出链接 (如果有封皮.作者.字数.页数等信息更佳)或表格,展示经 ...

  2. Linux命令(二十) 显示系统内存状态 free

    一.命令简介 free 命令会显示内存的使用情况,包括实体内存,虚拟的交换文件内存.共享内存区段,以及系统核心使用的缓冲区等. 二.参数说明 -b 以Byte为单位显示内存使用情况 -K 以KB为单位 ...

  3. Struts2 分割字符串标签s:generator

    有些时候会从后台返回一个字符串,可以通过Strut2的标签s:generator进行分割. generator标签,该标签可以将指定字符串按指定分隔符分割成多个字串.生成的多个字串可以用iterato ...

  4. Tour HDU - 3488(最大权值匹配)

    Tour In the kingdom of Henryy, there are N (2 <= N <= 200) cities, with M (M <= 30000) one- ...

  5. 【BZOJ4800】[Ceoi2015]Ice Hockey World Championship (meet in the middle)

    [BZOJ4800][Ceoi2015]Ice Hockey World Championship (meet in the middle) 题面 BZOJ 洛谷 题解 裸题吧,顺手写一下... #i ...

  6. Mountainous landscape

    Description 现在在平面上给你一条折线 \(P_1P_2 \cdots P_n\) . \(x\) 坐标是严格单调递增的.对于每一段折线 \(P_iP_{i+1}\) ,请你找一个最小的 \ ...

  7. 网络传输---HttpURLConnection

    HttpURLConnection是java做网络传输的一种,一般用于做数据的传输如xml数据传输 1.创建及配置: 1.1创建一个url对象,并指定url的地址 URL url = new URL( ...

  8. gulp入门教程(转)

    一.gulp简介     1.gulp是什么? gulp是前端开发过程中一种基于流的代码构建工具,是自动化项目的构建利器:它不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自 ...

  9. RabbitMQ 客户端开发向导

    准备工作:composer 引入 php-amqplib 说明:本文说明基于 Java(主要说明原理),实现使用 php RabbitMQ Java 客户端使用 com.rabbitmq.client ...

  10. 71. Simplify Path(M)

    71. Simplify Path Given an absolute path for a file (Unix-style), simplify it. For example, path = & ...