265°

Springcloud之zuul的zuulFilter

    Springcloud的版本是Greenwich.SR2,Springboot版本是2.1.6.release.

    最近使用到Springcloud的zuul,分析了下源码,记录下。

    如下List-1,我们自己定义的ZuulFilter继承zuul的zuulFilter,之后定义为Bean,交给Spring容器:

    List-1

//将过滤器交给Spring管理
@Bean
public AuthFilter authFilter(){
    return new AuthFilter();
}

//xss过滤 @Bean public XssFilter xssFilter(){ return new XssFilter(); }

@Bean public HelloZuulFilter firewallFilter(){ return new HelloZuulFilter(); }

@Bean public HelloInfoFilter helloInfoFilter(){ return new HelloInfoFilter(); }

    之后看下ZuulServerAutoConfiguration,如下List-2,@Autowired private Map<String, ZuulFilter> filters会从Spring容器中获取所有的ZuulFilter,之后实例化ZuulFilterInitializer时,将这个filters传入。

    List-2

...
@Configuration
protected static class ZuulFilterConfiguration {
@Autowired
private Map&lt;String, ZuulFilter&gt; filters;

@Bean
public ZuulFilterInitializer zuulFilterInitializer(CounterFactory counterFactory,
        TracerFactory tracerFactory) {
    FilterLoader filterLoader = FilterLoader.getInstance();
    FilterRegistry filterRegistry = FilterRegistry.instance();
    return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory,
            filterLoader, filterRegistry);
}

} ...

    如下List-3,

  1. FilterRegistry是属性类型,contextInitialized方法上加了@PostConstruct,所以Spring创建这个Bean之后就会调用这个方法,遍历filters之后,放入filterRegistry中,filterRegistry中有个ConcurrentHashMap类型的属性,这些filter就是放入这个ConcurrentHashMap中。
  2. 方法contextDestroyed上加了@PreDestroy注解,之后遍历filters,将其从filterRegistry中移除。

    List-3

public class ZuulFilterInitializer {
	private static final Log log = LogFactory.getLog(ZuulFilterInitializer.class);
	private final Map<String, ZuulFilter> filters;
	private final CounterFactory counterFactory;
	private final TracerFactory tracerFactory;
	private final FilterLoader filterLoader;
	private final FilterRegistry filterRegistry;
public ZuulFilterInitializer(Map&lt;String, ZuulFilter&gt; filters,
		CounterFactory counterFactory, TracerFactory tracerFactory,
		FilterLoader filterLoader, FilterRegistry filterRegistry) {
	this.filters = filters;
	this.counterFactory = counterFactory;
	this.tracerFactory = tracerFactory;
	this.filterLoader = filterLoader;
	this.filterRegistry = filterRegistry;
}

@PostConstruct
public void contextInitialized() {
	log.info("Starting filter initializer");

	TracerFactory.initialize(tracerFactory);
	CounterFactory.initialize(counterFactory);

	for (Map.Entry&lt;String, ZuulFilter&gt; entry : this.filters.entrySet()) {
		filterRegistry.put(entry.getKey(), entry.getValue());
	}
}

@PreDestroy
public void contextDestroyed() {
	log.info("Stopping filter initializer");
	for (Map.Entry&lt;String, ZuulFilter&gt; entry : this.filters.entrySet()) {
		filterRegistry.remove(entry.getKey());
	}
	clearLoaderCache();

	TracerFactory.initialize(null);
	CounterFactory.initialize(null);
}

...

    默认会将ZuulServlet或者ZuulServletFilter注入到Spring容器中,如下如果设置zuul.use-filter为true,那么使用的是ZuulServletFilter,默认没有设置zuul.use-filter,所以使用的是ZuulServlet,如下List-4,ZuulServlet继承了HttpServlet,是个Servlet,之后交给ServletRegistrationBean,将这个ZuulServlet放入到web容器中。

    List-4

...
@Bean
@ConditionalOnMissingBean(name = "zuulServlet")
@ConditionalOnProperty(name = "zuul.use-filter", havingValue = "false", matchIfMissing = true)
public ServletRegistrationBean zuulServlet() {
    ServletRegistrationBean<ZuulServlet> 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;
}

@Bean @ConditionalOnMissingBean(name = "zuulServletFilter") @ConditionalOnProperty(name = "zuul.use-filter", havingValue = "true", matchIfMissing = false) public FilterRegistrationBean zuulServletFilter() { final FilterRegistrationBean<ZuulServletFilter> filterRegistration = new FilterRegistrationBean<>(); filterRegistration.setUrlPatterns( Collections.singleton(this.zuulProperties.getServletPattern())); filterRegistration.setFilter(new ZuulServletFilter()); filterRegistration.setOrder(Ordered.LOWEST_PRECEDENCE); // The whole point of exposing this servlet is to provide a route that doesn't // buffer requests. filterRegistration.addInitParameter("buffer-requests", "false"); return filterRegistration; } ...

    ZuulServlet的service方法如下,首先会调用preRoute()方法,之后调用route(),最后是postRoute(),preRoute方法调用了zuulRunner.preRoute(),ZuulRunner的preRoute()方法里面调用了FilterProcessor.getInstance().preRoute(),再深入FilterProcessor的preRoute方法,

    List-5

public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    try {
        this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();
    try {
        this.preRoute();
    } catch (ZuulException var13) {
        this.error(var13);
        this.postRoute();
        return;
    }

    try {
        this.route();
    } catch (ZuulException var12) {
        this.error(var12);
        this.postRoute();
        return;
    }

    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(); }

    FilterProcessor的preRoute()里面的代码如下List-6,调用runFilters方法,从FilterRegistry取出所有filterType是pre的所有ZuulFilter,之后进行排序,之后逐个调用ZuulFilter的runFilter方法——在方法processZuulFilter里面。ZuulFilter是个抽象类,runFilter方法里面调用了run方法,run方法是抽象方法,由我们自定义实现,如下List-7所示,

    List-6

...
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());
    }
}

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

boolean bResult = false;
List&lt;ZuulFilter&gt; list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
    for(int i = 0; i &lt; list.size(); ++i) {
        ZuulFilter zuulFilter = (ZuulFilter)list.get(i);
        Object result = this.processZuulFilter(zuulFilter);
        if (result != null &amp;&amp; result instanceof Boolean) {
            bResult |= (Boolean)result;
        }
    }
}

return bResult;

}

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();
    }

    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:
        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);
        }
    }

    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;
    }
}

} ...

    如下List-7所示,用try catch方式来调用run方法,如果run方法抛出异常,则视为失败,将ZuulFilterResult的ExecutionStatus设置为FAILED,所以我们实现的run方法返回什么值并不重要,重要的是不抛出异常,如果抛出异常则视为处理失败。

    List-7

public ZuulFilterResult runFilter() {
    ZuulFilterResult zr = new ZuulFilterResult();
    if (!this.isFilterDisabled()) {
        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;

}

    要注意的是,上面中涉及到的FilterRegistry引用的都是同一个静态变量,所以各个调用关系见不显示的传递,依然能确保线程安全。

private static final FilterRegistry INSTANCE = new FilterRegistry();

Reference

  1. 源码

本文由【克虏伯】发布于开源中国,原文链接:https://my.oschina.net/u/2518341/blog/3068771

全部评论: 0

    我有话说: