springcloud Zuul中异常处理细节
Spring Cloud Zuul对异常的处理整体来说还是比较方便的,流程也比较清晰,只是由于Spring Cloud发展较快,各个版本之间有差异,导致有的小伙伴在寻找这方面的资料的时候经常云里雾里,本文将以Dalston.SR3版本为例,来说明Spring Cloud Zuul中的异常处理问题。
首先我们来看一张官方给出的Zuul请求的生命周期图,如下:
关于这张图我说如下几点:
正常情况下所有的请求都是按照pre、route、post的顺序来执行,然后由post返回response
在pre阶段,如果有自定义的过滤器则执行自定义的过滤器
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 != 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对象中提取出来,核心代码如下:
|
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
|
@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;} |
如果我们想要自定义异常信息也很方便,如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
|
@Componentpublic 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中异常处理细节的更多相关文章
- springcloud Zuul中路由配置细节
上篇文章我们介绍了API网关的基本构建方式以及请求过滤,小伙伴们对Zuul的作用应该已经有了一个基本的认识,但是对于路由的配置我们只是做了一个简单的介绍,本文我们就来看看路由配置的其他一些细节. 首先 ...
- SpringCloud学习之Zuul统一异常处理及回退
一.Filter中统一异常处理 其实在SpringCloud的Edgware SR2版本中对于ZuulFilter中的错误有统一的处理,但是在实际开发当中对于错误的响应方式,我想每个团队都有自己的处理 ...
- SpringCloud+ZUUL跨域请求中的OPTIONS请求处理
目前项目结构是VUE做前端,后端采用微服务架构,在开发时前端需要跨域请求数据,通过CorsConfig配置解决了简单跨域请求需要.但当需要在请求的header中增加token信息时,出现了请求失败的情 ...
- Spring cloud Zuul网关异常处理
Spring cloud Zuul网关异常处理 一 异常测试: 1> 创建一个pre类型的过滤器,并在该过滤器的run方法实现中抛出一个异常.比如下面的实现,在run方法中调用的doSometh ...
- SpringCloud Zuul 路由映射规则配置
阅读目录 前言 快速入门 路由详解 Cookie与头信息 本地跳转 Hystrix和Ribbon支持 过滤器解释 动态加载 后记 回到目录 前言 本文起笔于2018-06-26周二,接了一个这周要完成 ...
- springcloud Zuul学习笔记
SpringCloud Zull是一个基于NetflixZuul实现的API网关组件,它实现了请求路由,负载均衡,校验过滤等功能;本文主要记录springcloud zuul的入门级demo开发过程; ...
- 服务网关zuul之三:zuul统一异常处理
我们详细介绍了Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不同作用.我们会发现在这些核心过滤器中并没有实现error阶段的过滤器.那么这些过滤器可以用 ...
- Nacos(四):SpringCloud项目中接入Nacos作为配置中心
前言 通过前两篇文章: Nacos(二):Nacos与OpenFeign的对接使用 Nacos(三):SpringCloud项目中接入Nacos作为注册中心 相信大家已经对Nacos作为注册中心的基本 ...
- spring-cloud zuul网关
API Gateway 是随着微服务(Microservice)这个概念一起兴起的一种架构模式,它用于解决微服务过于分散,没有一个统一的出入口进行流量管理的问题. 使用 Zuul 实现 API Gat ...
随机推荐
- Vue 和 angular
vue适合移动端的项目,而angular更适合运用于Pc端的项目.
- final版本发布评价II
其实我对技术上的问题了解不多,所以有些评语可能说的不对或者压根就没啥用.可直接忽略.请见谅. 1新蜂的俄罗斯方块,UI设计虽然给出了背景和颜色,但是感觉色彩对比也不好,模块之间也不协调.没有更多的说服 ...
- jsonp 接口
一.请求接口 <script type="text/javascript" src="js/jquery.min.js"></script&g ...
- blog 社会化评论插件 多说for china, disqus for global range
1.disqus https://disqus.com/ https://publishers.disqus.com/engage https://disqus.com/admin/create/ h ...
- JSP生成静态Html页面
[转载]JSP生成静态Html页面 在网站项目中,为了访问速度加快,为了方便百度爬虫抓取网页的内容,需要把jsp的动态页面转为html静态页面.通常有2种常用的方式: 1.伪静态,使用URL Rewr ...
- MT【103】二阶递推找规律
评:如果直接找$a_n$的二阶递推式:$a_{n+2}-2\sqrt{2}a_{n+1}-a_n=0$有根号,不利于估计尾数.
- 【刷题】LOJ 6010 「网络流 24 题」数字梯形
题目描述 给定一个由 \(n\) 行数字组成的数字梯形如下图所示.梯形的第一行有 \(m\) 个数字.从梯形的顶部的 \(m\) 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶至 ...
- c++并发编程之原子操作的实现原理
原子(atomic)本意是”不能被进一步分割的最小粒子”,而原子操作(atomic operation)意为”不可被中断的一个或一系列操作”. 处理器如何实现原子操作 (1) 使用总线锁保证原子性 如 ...
- Linux上shell脚本,字符串转ASCII码
在shell脚本里,将字符串转ASCII码的方法: [keysystem@localhost ~]$ printf "%d" "'A" [keysystem@l ...
- LeetCode 9 合并两个有序列表
将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1->2- ...