1、过滤器概述
Spring Cloud Zuul包含了对请求的路由和过滤两个功能,其中路由负责将请求转发到指定的微服务上, 过滤器功负责对请求的处理过程进行干预,能够在路由HTTP请求和响应的过程中执行一系列操作,例如检测等
前面说到了过滤器有四个主要特征,继承ZuulFilter后都会重写其特征
- 类型:通常定义路由流程中使用过滤器的阶段 (字符串)
- 执行顺序:定义多个过滤器的执行顺序,值越小,执行顺序越优先
- 执行条件:执行过滤器所需要的条件
- 行为:过滤器满足条件的时候触发的行为操作
Zuul 提供了一个框架来动态读取、编译和运行这些过滤器,过滤器不相互铜线而是通过每个请求特有的请求上下文共享状态
2、过滤器类型
现阶段有四种过滤器类型,过滤器声明周期如上图所示
- PRE: 路由请求到指定服务实例之前过滤器被执行,例如包括请求身份验证,选择服务器和记录调试信息
- ROUTING:路由请求到服务实例时过滤器被执行
- POST: 过滤器在请求被路由到服务实例之后过滤器被执行,例如向相应中添加标准HTTP头
- ERROR: 当再其他某个阶段出现错误的时候,过滤器被执行
3、过滤器使用
Sping Cloud Zuul中实现的过滤器包含上述四个基本特征,即过滤器类型,执行顺序,执行条件,行为操作,实际上它就是ZuulFilter 接口定义的抽象方法,所以自定义过滤器只需要写一个过滤器继承 ZuulFilter抽象类并实现抽象方法即可,
测试代码见: Spring Cloud Zuul 使用快速入门
public class AccessFilter extends ZuulFilter {
private static Logger log = LoggerFactory.getLogger(AccessFilter.class);
/**
* pre - 前置过滤器,在请求被路由前执行,通常用于处理身份认证,日志记录等;
* route - 在路由执行后,服务调用前被调用;
* error - 任意一个filter发生异常的时候执行或远程服务调用没有反馈的时候执行(超时),通常用于处理异常;
* post - 在route或error执行后被调用,一般用于收集服务信息,统计服务性能指标等,也可以对response结果做特殊处理
*
* @return
*/
@Override
public String filterType() {
return "pre";
}
/**
* 同类型过滤器自然顺序执行
* 返回值越小,执行顺序越优先
*
* @return
*/
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
Object accessTocken = request.getParameter("tocken");
if (accessTocken != null) {
//1.测试过滤器验证,只有888888返回401
if ("888888".equals(accessTocken)) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
return null;
}
//2.测试直接拋出自定义异常
if ("000000".equals(accessTocken)) {
doSomeThing();
return null;
}
//3.测试直接抛出 ZuulRuntimeException 异常
if ("111111".equals(accessTocken)) {
throw new ZuulRuntimeException ("In AccessFilter,Error testing");
}
//4.测试try-catch
if ("123456".equals(accessTocken)) {
try {
throw new RuntimeException("In AccessFilter,Error testing,");
} catch (Exception e) {
requestContext.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
requestContext.setThrowable(e);
}
return null;
}
return null;
}
return null;
}
private void doSomeThing() {
log.error("AccessFilter error");
throw new RuntimeException("In AccessFilter,Error testing,");
}
}
4、过滤器执行流程
过滤器执行处理时序图 ,
1、 ZuulServerAutoConfiguration定义了ServletRegistrationBean,将请求交给ZuulServlet来处理;
public class ZuulServerAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
public ServletRegistrationBean zuulServlet() {
ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
this.zuulProperties.getServletPattern());
// The whole point of exposing this servlet is to provide a route that doesn't
// buffer requests.
servlet.addInitParameter("buffer-requests", "false");
return servlet;
}
//略
}
2、 ZuulServlet提供pre,routing,post三个阶段处理方法,调用ZuulRunner对象实现;
- zuulRunner.preRoute()
- zuulRunner.route()
- zuulRunner.postRoute()
@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
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();
}
}
//通过执行器FilterProcessor处理
public void preRoute() throws ZuulException {
FilterProcessor.getInstance().preRoute();
}
public void route() throws ZuulException {
FilterProcessor.getInstance().route();
}
public void postRoute() throws ZuulException {
FilterProcessor.getInstance().postRoute();
}
public void error() {
FilterProcessor.getInstance().error();
}
3、 过滤执行器FilterProcessor先通过过滤器类型字符串“pre”、“route”、“pre”、“post”、“error”从FilterLoader得到指定类型的过滤器并排序,因为ZuulFilter实现了接口Comparable,按照filterOrder方法提供的值排序,因此值越小,执行顺序越优先,获取所有满足条件的过滤器后,循环处理通过processZuulFilter方法执行每一个过滤器;
//FilterProcessor 过滤器处理源码
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
//获取所有指定类型的过滤器
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
//循环处理每一个过滤器
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
//FilterLoader中根据过滤器类型过去过滤器并排序
public List<ZuulFilter> getFiltersByType(String filterType) {
List<ZuulFilter> list = hashFiltersByType.get(filterType);
if (list != null) return list;
list = new ArrayList<ZuulFilter>();
Collection<ZuulFilter> filters = filterRegistry.getAllFilters();
for (Iterator<ZuulFilter> iterator = filters.iterator(); iterator.hasNext(); ) {
ZuulFilter filter = iterator.next();
if (filter.filterType().equals(filterType)) {
list.add(filter);
}
}
Collections.sort(list); // sort by priority
hashFiltersByType.putIfAbsent(filterType, list);
return list;
}
//ZuulFilter对象中实现了Comparable接口自定义按照filterOrder排序顺序排序
public int compareTo(ZuulFilter filter) {
return Integer.compare(this.filterOrder(), filter.filterOrder());
}
4、 ZuulFilter过滤器运行时候,先执行isFilterDisable判断过滤器是否禁用,然后执行shouldFilter方法判断是否需要执行,如果执行调用run()执行过滤器过滤行为,这两个方法也是继承ZuulFilter后需要实现的抽象方法;
public ZuulFilterResult runFilter() {
ZuulFilterResult zr = new ZuulFilterResult();
//是否禁用了过滤器 zuul.<SimpleClassName>.<filterType>.disable=true/false
if (!isFilterDisabled()) {
// 抽象方法
if (shouldFilter()) {
Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName());
try {
Object res = run();
zr = new ZuulFilterResult(res, ExecutionStatus.SUCCESS);
} catch (Throwable e) {
t.setName("ZUUL::" + this.getClass().getSimpleName() + " failed");
zr = new ZuulFilterResult(ExecutionStatus.FAILED);
zr.setException(e);
} finally {
t.stopAndLog();
}
} else {
zr = new ZuulFilterResult(ExecutionStatus.SKIPPED);
}
}
return zr;
}
5、过滤器异常处理
过滤器SendErrorFilter是用来处理异常信息,上面说到了当shouldFilter返回true的时候才会真正处理此过滤器, shouldFilter不同版本实现有些许区别, 例如我用的版本是spring-cloud-zuul:1.3.6.RELEASE版本, 实现源码如下, 判断throwable不为空
//spring-cloud-zuul:1.3.6.RELEASE的源码
@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);
}
《Spring Cloud 微服务实战》 书中提到shouldFilter方法却不同
public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return ctx.containsKeys("error.status_code")
&& !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
}
因此一般情况如果异常过滤器没有处理,需要检查版本中的SendErrorFilter#shouldFilter实现代码,再考虑自定义过滤器编码中的问题
6、过滤器禁用方式
ZuulFilter过滤器运行时候,先执行isFilterDisable判断过滤器是否禁用,然后执行shouldFilter方法判断是否需要执行最后在执行过滤器行为,可以通过参数来禁用指定的过滤器,格式如下
###禁用过滤器
###格式:zuul.<SimpleClassName>.<pre>.disable=true
zuul.AccessFilter.pre.disable=true
因此当过滤器不确定是否暂时不同后续还会使用的情况时,建议通过参数先禁用此过滤器不需要修改代码
版权声明:「DDKK.COM 弟弟快看,程序员编程资料站」本站文章,版权归原作者所有