Spring Cloud网关服务Zuul过滤器流程

上文中得知,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 种不同生命周期的过滤器类型, 具体如下所示。
  1. pre: 可以在请求被路由之前调用,可用于登录校验等。
  2. routing: 在路由请求时被调用。
  3. post: 在 routing 和 error 过滤器之后被调用。
  4. 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.思考

  1. postRoute抛出的异常怎么处理返回结果

我们异常信息组织在sendErrorFilter中,他会将pre或route阶段保存在上下文中的错误信息、错误码进行组织,然后返回。 那么我们是否可以增加一个error过滤器,对错误信息结果也组装返回,起到sendErrorFilter的作用呢? 这样是可行,但我们在post和error阶段就存在两个一样作用的过滤器,就会在pre阶段抛出异常时,各执行一次。

更好的做法则是在error阶段,增加一个过滤器来实现sendErrorFilter来单独处理post阶段抛出异常。

  1. 怎么样判断是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错误端点的请求来产生错误响应。

end
  • 作者:Endwas(联系作者)
  • 发表时间:2021-07-13 17:26
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论