Zuul网关源码跟踪之Zuul四种过滤器的生命周期及执行异常时的细节探讨


本篇文章记录的是笔者在学习Zuul时对四大Filters的疑惑及求解过程。文章结尾有总结结论,可以直接看结论(所有内容均来自于源码跟踪后总结)。

四大过滤器类型及生命周期

Zuul的过滤器类型有四种:pre、route、post和error。
其中pre、route、post是正常的请求响应时必走的三大流程。并且,在整个请求响应过程中如果出现了异常没有被及时处理,那么异常被抛到了Zuul后,会由error类型过滤器进行处理。
此处就有了第一个分歧点。
网上很多课程or资料都说的是,post过滤器是负责将响应返回给客户端的,因此在error类型过滤器处理完后,会继续交给post过滤器进行处理并返回最终结果给客户端。这个观点,只说对了一半。
事实上,根据源码,在pre、route类型的过滤器执行过程中若出现了异常,确实是会先经由error类型过滤器处理,然后再交给post过滤器处理。
但若是post类型过滤器执行过程中出现了异常,那么就只会由error类型过滤器进行后续处理,并直接返回响应内容给客户端,不会再次经过post类型过滤器
(想想也觉得合理,post都抛异常了,再经过post不是又要抛异常吗?意义何在)。

整个生命周期的图示如下(重点):
Zuul过滤器的生命周期
对于该生命周期结论的获取,由ZuulServlet.service()中的源码所得:

public class ZuulServlet extends HttpServlet {
    private static final long serialVersionUID = -3374242278843351500L;
    private ZuulRunner zuulRunner;

    public ZuulServlet() {
    }

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        String bufferReqsStr = config.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");
        this.zuulRunner = new ZuulRunner(bufferReqs);
    }

	// !!!重点在这里!!!
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        try {
            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();
			
			// 执行 pre过滤器,如果抛异常,则在catch中执行 error过滤器 和 post过滤器
            try {
                this.preRoute();
            } catch (ZuulException var13) {
                this.error(var13);
                this.postRoute();
                return;
            }

			// 执行 route过滤器,如果抛异常,则在catch中执行 error过滤器 和 post过滤器
            try {
                this.route();
            } catch (ZuulException var12) {
                this.error(var12);
                this.postRoute();
                return;
            }

			// 执行 post过滤器,如果抛异常,则在catch中执行 error过滤器
			// 注意!此处的catch中并没有再次执行 post过滤器!
            try {
                this.postRoute();
            } catch (ZuulException var11) {
                this.error(var11);
            }
        } catch (Throwable var14) {
            this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

    void postRoute() throws ZuulException {
        this.zuulRunner.postRoute();
    }

    void route() throws ZuulException {
        this.zuulRunner.route();
    }

    void preRoute() throws ZuulException {
        this.zuulRunner.preRoute();
    }

    void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        this.zuulRunner.init(servletRequest, servletResponse);
    }

    void error(ZuulException e) {
        RequestContext.getCurrentContext().setThrowable(e);
        this.zuulRunner.error();
    }
}

通过上述的源码,我们可以看到,Zuul本质上也是个Servlet,在重写Service()中实现了对请求的相关处理。
其中,通过三个try-catch分别对pre、route和post类型的过滤器进行调用,在调用过程中若出现了异常,则由catch中显式调用的过滤器(如post、error)做相应的后续处理。其中,post过滤器执行过程中若抛异常,则只会由error过滤器进行处理,这点与pre过滤器、route过滤器不同。

同一类型的过滤器的执行细节

对于同一类型的过滤器,如多个pre类型的过滤器,通过上述代码我们也可以看出,Zuul会先执行完所有的pre类型过滤器,然后再执行route类型过滤器,以此类推。
在同类型的过滤器链中,根据过滤器中的:

	public abstract int filterOrder();

该方法的返回值来决定执行顺序,数字越小,优先级越高,越先执行。

那么,疑问来了。对于同一类型的过滤器形成的过滤器链,当其中某一个过滤器抛异常了,剩余的过滤器还会接着执行吗?
还是根据源码来分析,此处我们以pre类型过滤器链为例:

  1. 先从ZuulServlet.service()中的preRoute()进入
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        try {
            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
            	//  第1步
                this.preRoute();
            } catch (ZuulException var13) {
                this.error(var13);
                this.postRoute();
                return;
            }
			
			// 其余无关源码已删除省略
           	...... 
    }

// 调用了zuulRunner
void preRoute() throws ZuulException {
        this.zuulRunner.preRoute();
	}
  1. 调用(Zuul运行器)ZuulRunner.preRoute(),该方法源码如下:
public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }
  1. 调用(过滤器执行器)FilterProcessor.preRoute(),该方法源码如下:
public void preRoute() throws ZuulException {
        try {
        	// 重点看这一个方法
            this.runFilters("pre");
        } catch (ZuulException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
        }
    }

继续调用该类中的runFilters(),传入的参数为"pre",代表本次执行的pre类型的过滤器:

public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }

        boolean bResult = false;
        // 根据传入的参数值,利用过滤器加载器 FilterLoader 获取指定类型("pre")的所有过滤器
        List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
		// 对获取的过滤器列表进行循环遍历
        if (list != null) {
            for(int i = 0; i < list.size(); ++i) {
                ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
                // 执行列表中的每一个过滤器
                Object result = this.processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= (Boolean)result;
                }
            }
        }

        return bResult;
    }
  1. FilterProcessor.processZuulFilter()执行列表中的每一个过滤器
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        boolean bDebug = ctx.debugRouting();
        String metricPrefix = "zuul.filter-";
        long execTime = 0L;
        String filterName = "";

        try {
            long ltime = System.currentTimeMillis();
            filterName = filter.getClass().getSimpleName();
            RequestContext copy = null;
            Object o = null;
            Throwable t = null;
            if (bDebug) {
                Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);
                copy = ctx.copy();
            }
			
			// 调用具体的Filter的runFilter(),并获取结果
            ZuulFilterResult result = filter.runFilter();
            // 获取此次结果的状态,有 FAILED 和 SUCCESS两种
            ExecutionStatus s = result.getStatus();
            execTime = System.currentTimeMillis() - ltime;
            switch (s) {
                case FAILED:
                	// 过滤器执行有问题,抛出异常,将异常赋予变量t
                    t = result.getException();
                    ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                    break;
                case SUCCESS:
                    o = result.getResult();
                    ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
                    if (bDebug) {
                        Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
                        Debug.compareContextState(filterName, copy);
                    }
            }

			// 当变量t 确实保存了异常对象时,将其抛出给调用者
            if (t != null) {
                throw t;
            } else {
                this.usageNotifier.notify(filter, s);
                return o;
            }
        } catch (Throwable var15) {
            if (bDebug) {
                Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + var15.getMessage());
            }

            this.usageNotifier.notify(filter, ExecutionStatus.FAILED);
            if (var15 instanceof ZuulException) {
                throw (ZuulException)var15;
            } else {
                ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);
                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                throw ex;
            }
        }
    }
  1. 具体的Filter的公共runFilter()如下:
    该方法属于ZuulFilter,而所有的Filter都需要继承ZuulFilter,因此这是所有Filter的通用流程。
public ZuulFilterResult runFilter() {
        ZuulFilterResult zr = new ZuulFilterResult();
        if (!this.isFilterDisabled()) {
        	// 该过滤器需要执行时的逻辑如下
            if (this.shouldFilter()) {
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());

                try {
                	// 调用具体的过滤器重写的run(),并获取结果
                    Object res = this.run();
                    // 如果run()正常执行,那么说明过滤器执行没有问题,返回一个 SUCCESS状态的结果
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable var7) {
                    t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                    // 如果run()执行过程中抛出了异常,那么返回一个FAILED状态的结果
                    zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                    // 同时将异常封装进结果中
                    zr.setException(var7);
                } finally {
                    t.stopAndLog();
                }
            } else {
            	// 该过滤器不需要执行时,直接返回一个 标记为 SKIPPED状态的结果
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }

        return zr;
    }

通过以上5步的源码,我们可以看出,从最初的ZuulServlet.service()开始不断调用,最后到了具体的Filter.runFilter(),这就是每一个过滤器的详细调用流程。

那么,再次回到我们的问题,通过第3步的源码我们已经知道,Zuul会先获取同一类型的所有过滤器形成过滤器列表,并对列表进行循环遍历调用。 那么,如果同一类型的某个Filter运行出现异常,那么同类型的其他过滤器是否还会遍历执行?

答案是:
当出现异常后,同一类型的剩余过滤器不会被遍历执行,该类型的过滤器调用链会被中断,直接返回到ZuulServlet.service()中,由各个类型的catch{}进行后续的异常处理。

根据源码做如下解释:

  1. 如果在ZuulFilter.runFilter()中调用run()出了异常,那么该异常会被catch,在catch中会封装一个状态为FAILED的结果,并将异常封装在结果中;
  2. 第1步的调用结果会返回到FilterProcessor.processZuulFilter()中,FilterProcessor获取到一个状态为FAILED的结果,在switch中符合第一种情况(case FAILED),并做如下处理:
			switch (s) {
                case FAILED:
                	// 过滤器执行有问题,将异常赋予变量t
                    t = result.getException();
                    ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);
                    break;
                case SUCCESS:
                    o = result.getResult();
                    ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);
                    if (bDebug) {
                        Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");
                        Debug.compareContextState(filterName, copy);
                    }
            }

并在switch代码块之后,对变量t进行空值判断,若有值,代表获取到了异常,说明本次调用失败,所以继续将该异常抛出给调用者。

			// 当变量t 确实保存了异常对象时,将其抛出给调用者
            if (t != null) {
                throw t;
            } else {
                this.usageNotifier.notify(filter, s);
                return o;
            }
  1. 于是调用栈回到了FilterProcessor.runFilters()的循环遍历位置:
        if (list != null) {
            for(int i = 0; i < list.size(); ++i) {
                ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
                // 调用栈回到了此处,收到了个异常
                Object result = this.processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= (Boolean)result;
                }
            }
        }
  1. 由于该方法runFilters()并没有做try-catch处理,因此异常会导致该方法的执行被迫中断,因此对list的遍历也就中断了。 所以到此,我们的结论也就被源码给实锤了。
  2. 接着FilterProcessor.preRoute()虽然捕获了runFilters()的异常,但也在catch中对它进行再次抛出,一直往上回调到ZuulServlet.service()中,被catch给捕获并进行处理:
			try {
            	//  抛出异常
                this.preRoute();
            } catch (ZuulException var13) {
            	// 捕获异常,交给error过滤器链和post过滤器链处理
                this.error(var13);
                this.postRoute();
                // 并在处理结束后返回
                return;
            }

如果error类型过滤器抛异常了,谁兜底呢?

答案是:没有人兜底。因为FilterProcessor在调用error类型过滤器时会自己把异常给消化掉,不再向上抛出给调用者。
从刚刚的第二大结论的探讨中,我们发现ZuulServlet在调用过滤器的时候,会先经过FilterProcessor,由它根据不同的过滤器类型去获取不同的过滤器列表并调用执行(this.runFilters(过滤器类型))。

在FilterProcessor中的相关源码如下:

public class FilterProcessor {
    static FilterProcessor INSTANCE = new FilterProcessor();
    protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);
    private FilterUsageNotifier usageNotifier = new BasicFilterUsageNotifier();

	// 省略部分无关方法代码
    .......

    public void postRoute() throws ZuulException {
        try {
            this.runFilters("post");
        } catch (ZuulException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + var3.getClass().getName());
        }
    }

    public void error() {
        try {
            this.runFilters("error");
        } catch (Throwable var2) {
        	// 对error类型过滤器的执行过程中抛出的异常
        	// 只进行捕获并打印日志,但不向上抛出
            logger.error(var2.getMessage(), var2);
        }

    }

    public void route() throws ZuulException {
        try {
            this.runFilters("route");
        } catch (ZuulException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + var3.getClass().getName());
        }
    }

    public void preRoute() throws ZuulException {
        try {
            this.runFilters("pre");
        } catch (ZuulException var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());
        }
    }

	// 省略部分无关方法代码
	.......
	
}

从源码可以看出,FilterProcessor的xxxRoute()针对不同的过滤器类型有不同的方法实现体,其中对异常的处理并不全都相同。
针对pre、route、post类型过滤器的执行调用,若runFilters()过程中出现异常,那么FilterProcessor会对异常进行捕获,但不进行消化处理,而是在catch中进行二次抛出。从而将异常向上传递,抛给调用者,最终传递到ZuulServlet.service()中。
而对于error类型过滤器,若是在执行runFilters(“error”)的过程中出现了异常,那么会对该异常进行catch,并调用日志打印器logger打印相关的异常信息,然后就结束了本次异常的处理。也就是说,不会将异常进行二次抛出,所以调用者如ZuulServlet对该异常是不知情的,因此也就没有相应的后续处理。

因此,若是error过滤器链在执行过程中出现了异常,是会被FilterProcessor自我消化的,不需要其他的组件来兜底。

异常时的输出到客户端的信息,是由谁来负责输出的?默认的post过滤器起到了什么作用?

Zuul的生命周期过程中出现异常时,
默认的error类型过滤器链只有一个过滤器,是:SendErrorFilter;
默认的post类型过滤器链也只有一个,是:SendResponseFilter。

整个请求响应的过程中若出现了异常,无论是pre、route还是post过滤器链执行出现了异常,最终都会由error过滤器链对异常响应信息进行封装处理并写入到response的writer(类型为:CoyoteWriter)中。
并且SendErrorFilter写完后会强制关闭writer,也就是说后续的其他error过滤器或post过滤器,包括我们自定义的这两种类型的过滤器,都无法对响应内容进行修改。
因为在Java中,一旦流被关闭,就意味着它已经不可用了,任何试图使用已关闭的流进行写操作都会抛出异常。因此,一旦CoyoteWriter被关闭,它通常就不能再被利用来向客户端输出响应数据。

在这里插入图片描述

这也就是为什么说,如果我们要自定义异常信息,即使我们自定义了error类型过滤器,它也无法将我们自定义的异常信息响应给客户端,也就是说无法发挥作用。
原因就在于Zuul默认的error过滤器SendErrorFilter在error过滤器链中的优先级最高,且它会强制关闭RequestContext.getCurrentContext().response的writer输出流对象,导出后续的过滤器都无法对响应内容进行再次修改,源码如下:

public class SendErrorFilter extends ZuulFilter {
    private static final Log log = LogFactory.getLog(SendErrorFilter.class);
    protected static final String SEND_ERROR_FILTER_RAN = "sendErrorFilter.ran";
    @Value("${error.path:/error}")
    private String errorPath;

    public SendErrorFilter() {
    }

	// error类型
    public String filterType() {
        return "error";
    }
	
	// 优先级最高
    public int filterOrder() {
        return 0;
    }
	
	// 其他方法省略
	......
}

因此,如果要让我们自定义的error类型过滤器发挥作用,实现能够对异常响应内容进行自定义,那我们就必须先禁用了Zuul默认的error过滤器也就是SendErrorFilter。
配置如下:

zuul:
  SendErrorFilter:   # 过滤器类名
    error:           # 过滤器类型
      disable: true  # 禁用该过滤器

注意!这里禁用Zuul默认的SendErrorFilter过滤器,是为了实现自定义异常响应内容(因为SendErrorFilter会把writer输出流给close)。 若是没有这方面的需求,是不需要开启这个配置的。即, SendErrorFilter的启用与否,并不会影响到我们自定义的error类型过滤器的执行,它只是影响到自定义的异常响应内容能否顺利响应到客户端。


再回到本话题的结论,SendErrorFilter会强制关闭response的writer,源码如何追踪?
由于这部分涉及的源码内容比较多,就不贴出来了,大家有兴趣的可以自定义一个过滤器并在run()抛出异常,然后debug,需要重点留意的位置(类名.方法名)是:

  1. SendErrorFilter.run():
			// 方法中的核心代码:
			RequestDispatcher dispatcher = request.getRequestDispatcher(this.errorPath);
            if (dispatcher != null) {
                ctx.set("sendErrorFilter.ran", true);
                if (!ctx.getResponse().isCommitted()) {
                    ctx.setResponseStatusCode(exception.getStatusCode());
                    // 对请求进行转发,转发到/error端点
                    dispatcher.forward(request, ctx.getResponse());
                }
            }
  1. ApplicationDispatcher.doForward():
		// 方法中的核心代码:
		if  (response instanceof ResponseFacade) {
            ((ResponseFacade) response).finish();
        } else {
            // Servlet SRV.6.2.2. The Request/Response may have been wrapped
            // and may no longer be instance of RequestFacade
            if (wrapper.getLogger().isDebugEnabled()){
                wrapper.getLogger().debug( " The Response is vehiculed using a wrapper: "
                           + response.getClass().getName() );
            }

            // Close anyway
            // !!!关键点在这里!!!
            try {
            	// 在这一步之前,会通过各种逻辑,将响应信息写入到response.writer中,
            	// 到了这一步后,就会强制将writer进行close(),
            	// 也就是关闭本次请求的最终响应的输出流对象,那么后续就无法再对响应内容进行任何修改操作
                PrintWriter writer = response.getWriter();
                writer.close();
            } catch (IllegalStateException e) {
                try {
                    ServletOutputStream stream = response.getOutputStream();
                    stream.close();
                } catch (IllegalStateException f) {
                    // Ignore
                } catch (IOException f) {
                    // Ignore
                }
            } catch (IOException e) {
                // Ignore
            }
        }

在关闭了response.writer后,SendErrorFilter的处理逻辑也就大体上都结束了,会逐层返回,最终回到ZuulServlet.service()中。
那么,对于pre、route类型的过滤器,既然SendErrorFilter已经将异常请求的响应内容设置好了,那么post类型过滤器在这里起到了什么作用呢?
答案是:默认的post类型过滤器(即SendResponseFilter)在这里起到了造型上的作用。
不信?那你往下看:
当调用ZuulFilter.runFilter()时,会对具体的过滤器进行开关判断,即判断该过滤器是否启用,源码如下:

public abstract class ZuulFilter implements IZuulFilter, Comparable<ZuulFilter> {
    // 省略其他无关代码
    ......
    
    public String disablePropertyName() {
    	// 在配置文件中的disable属性设置
        return "zuul." + this.getClass().getSimpleName() + "." + this.filterType() + ".disable";
    }

    public boolean isFilterDisabled() {
        this.filterDisabledRef.compareAndSet((Object)null, DynamicPropertyFactory.getInstance().getBooleanProperty(this.disablePropertyName(), false));
        return ((DynamicBooleanProperty)this.filterDisabledRef.get()).get();
    }

    public ZuulFilterResult runFilter() {
        ZuulFilterResult zr = new ZuulFilterResult();
        // 判断该过滤器是否在配置文件中被设置disable: true
        if (!this.isFilterDisabled()) {
        	// 判断该过滤器重写的shouldFilter()是否返回true
            if (this.shouldFilter()) {
                Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());

                try {
                    Object res = this.run();
                    zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
                } catch (Throwable var7) {
                    t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
                    zr = new ZuulFilterResult(ExecutionStatus.FAILED);
                    zr.setException(var7);
                } finally {
                    t.stopAndLog();
                }
            } else {
                zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
            }
        }

        return zr;
    }


	// 省略其他无关方法
	......
	
}

其中,对于异常发生时,catch中调用post过滤器链,默认是调用了SendResponseFilter,而该过滤器的shouldFilter()此时会是如下的情况:
SendResponseFilter的shouldFilter()
此时由于生命周期中有异常被捕获并放置在请求上下文(RequestContext)中,因此&&的前置条件是false,所以该方法会返回false,也就是不执行,所以ZuulFilter.runFilter()会进入else分支并最终返回一个状态为SKIPPED(译:跳过)的结果对象(return new ZuulFilterResult(ExecutionStatus.SKIPPED));


error类型过滤器的管辖范围

Zuul中的error类型过滤器,只会对pre、route和post类型过滤器的执行流程中的异常进行管理。
若是在调用远程服务API时,API的执行过程中出现了异常,那么Zuul是无法对此异常进行管理的(当然包括自定义的error过滤器的异常处理也同样不起作用),业务层的异常会交由Spring来管理。
如下图所示,是Zuul过滤器的整个生命周期流程图,其中虚线部分所圈住的范围,就是error类型过滤器的管辖范围。很明显,Origin Server中出现的异常,不归error类型过滤器管控和处理。

Zuul过滤器的生命周期



核心总结

  1. pre、route、post类型过滤器链执行过程中出现异常,都会由error过滤器链进行异常处理,但只有pre、route类型产生的异常会在error类型过滤器链处理后,再交由post类型过滤器链进行二次处理。
  2. FilterProcessor在调用pre、route、post三种类型的过滤器时,若出现异常,会将异常向上抛出,不消化异常。但在调用error类型过滤器链时,若出现异常,则直接进行catch打印相关日志信息,并不再向上抛出。
  3. 同一类型的过滤器链中,有一个过滤器执行时出现了异常,那么该类型过滤器链中的剩余过滤器,都不会再继续往下执行,而是将异常抛出直到ZuulServlet.service()的catch{}中。
  4. 请求过程中出现异常,那么本次请求所对应的响应中的异常信息,由error过滤器链进行控制和输出到response中,给到客户端。默认的post过滤器链会被跳过,不做任何操作。
  5. 自定义的异常处理error类型过滤器,若是涉及了自定义异常响应内容这方面的处理,必须先禁用了默认的SendErrorFilter才能发挥作用。
  6. error类型过滤器,只对Zuul的过滤器(包括pre、route和post三种类型的过滤器)执行过程中产生的异常有作用,无法处理在Origin Server即远程业务层服务的异常。


        好了,以上就是我个人对本次内容的理解与解析,如果有什么不恰当的地方,还望各位兄弟在评论区指出哦。
        如果这篇文章对你有帮助的话,不妨点个关注吧~
        期待下次我们共同讨论,一起进步~

  • 19
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值