上文中得知,Zuul核心用到就是过滤器模式,里面无论路由映射还是请求转发都需要使用到Zuul过滤器。所以,过滤器可以说是Zuul实现API网关功能最为核心的部件,每一个进入Zuul的HTTP请求都会经过一系列的过滤器处理链得到请求响应并返回给客户端。
1.过滤器接口
在Spring Cloud Zuul中实现的过滤器接口ZuulFilter必须包含4个基本特征: 过滤类型、 执行顺序、执行条件、 具体操作。
// 过滤类型 pre/post等
String filterType();
// 过滤器优先级
int filterOrder();
// 是否应该过滤
boolean shouldFilter();
// 运行过滤器逻辑
Object run();
- filterType: 该函数需要返回一个字符串来代表过滤器的类型, 而这个类型就是在HTTP请求过程中定义的各个阶段。在 Zuul 中默认定义了 4 种不同生命周期的过滤器类型, 具体如下所示。
- pre: 可以在请求被路由之前调用,可用于登录校验等。
- routing: 在路由请求时被调用。
- post: 在 routing 和 error 过滤器之后被调用。
- error: 处理请求时发生错误时被调用。
- filterOrder: 通过 int 值来定义过滤器的执行顺序,数值越小优先级越高。
- shouldFilter: 返回一个 boolean 值来判断该过滤器是否要执行。 我们可以通过此方法来指定什么情况需要被执行。
- run: 过滤器的具体逻辑。在该函数中,我们可以实现自定义的过滤逻辑, 来确定是否要拦截当前的请求,不对其进行后续的路由, 或是在请求路由返回结果之后,对处理结果做一些加工等。
2.核心过滤器
在zuul中已经包含很多默认的过滤器,他们作用在不同生命周期中,我们在z org.springframework.cloud.netflix.zuul.filters包下。可以看到这些过滤器。
下面是过滤器的执行时机,优先级和功能。
3.请求生命周期
我们可以看到在调用远程服务前会执行prefilters,在调用远程服务后会调用postfilters,如果在路由过滤器routingfilters时遇到错误,会调用errorfilters,当然无论在正常、异常执行路由过滤器后都会执行postfilters。
我们从源码看一下不同过滤器执行时候,各个阶段的过滤器如何进行调度的。
try {
// pre过滤器
preRoute();
) catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
// 路由过滤器
route();
} catch (ZuulException e) {
error(e);
postRoute();
return;
}
try {
// post过滤器
postRoute(} ;
} catch (ZuulException e} {
error(e};
// 不需要执行postRoute过滤器
return;
}
我们可以看到在pre、route阶段的过滤器遇到异常会执行postRoute和error阶段过滤器,但postRoute阶段过滤器执行异常,就只会执行error过滤器了,我们前文可以得知,postRoute中sendErrorFilter是用来组织返回结果的,如果没有执行post过滤器,那么我们就无法得到返回结果,那么我们该如何处理?
4.思考
- postRoute抛出的异常怎么处理返回结果
我们异常信息组织在sendErrorFilter中,他会将pre或route阶段保存在上下文中的错误信息、错误码进行组织,然后返回。 那么我们是否可以增加一个error过滤器,对错误信息结果也组装返回,起到sendErrorFilter的作用呢? 这样是可行,但我们在post和error阶段就存在两个一样作用的过滤器,就会在pre阶段抛出异常时,各执行一次。
更好的做法则是在error阶段,增加一个过滤器来实现sendErrorFilter来单独处理post阶段抛出异常。
- 怎么样判断是post类型过滤器
我们要如何实现shouldFilter方法,控制他只处理post抛出的异常。 我们首先是想到在上下文对象中找到是否在抛出异常时候会存储,但很遗憾并没有保存下来。所以我们就需要拓展处理器。在处理器执行各个阶段过滤器时若抛出异常,将filter进行存储。
public class ExtendFilterProcessor extends FilterProcessor {
@Override
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
try {
// 不改变父执行器的逻辑,只是增加异常捕获,将过滤器放到上下文中
return super.processZuulFilter(filter);
} catch (ZuulException e) {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.set("failed.filter", filter);
throw e;
}
}
}
然后继承SendErrorFilter来处理post阶段抛出的异常。
@Component
public class ErrorExtFilter extends SendErrorFilter {
@Override
public String filterType () {
return "error";
@Override
public int filterOrder() {
return 30; //大于ErrorFilter的值
@Override
public boolean shouldFilter() {
//判断:仅处理来自post过滤器引起的异常
RequestContext ctx = RequestContext.getCurrentContext();
ZuulFilter failedFilter = (ZuulFilter) ctx.get("failed.filter");
if(failedFilter != null && failedFilter.filterType().equals ("post")) {
return true;
}
return false;
}
// run
}
总结
Spring Cloud zuul的过滤器组件功能很强大,但他在实现时候并不是很友好,我们需要对其完善,否则有可能会出现拿不到想要结果情况。过滤器异常处理核心两个filter,第一个是Errorfilter它是属于error阶段的,在遇到异常时候,对异常信息进行封装到上下文对象的。第二个是sendErrorFilter它是属于post阶段的,它利用请求上下文中的错误信息来组成一个forward到API网关/error错误端点的请求来产生错误响应。
评论