您好,登錄后才能下訂單哦!
Springcloud中zuul的Zuul Filter是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
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<String, ZuulFilter> 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,
FilterRegistry是屬性類型,contextInitialized方法上加了@PostConstruct,所以Spring創建這個Bean之后就會調用這個方法,遍歷filters之后,放入filterRegistry中,filterRegistry中有個ConcurrentHashMap類型的屬性,這些filter就是放入這個ConcurrentHashMap中。
方法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<String, ZuulFilter> 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<String, ZuulFilter> entry : this.filters.entrySet()) { filterRegistry.put(entry.getKey(), entry.getValue()); } } @PreDestroy public void contextDestroyed() { log.info("Stopping filter initializer"); for (Map.Entry<String, ZuulFilter> 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<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType); if (list != null) { for(int i = 0; i < list.size(); ++i) { ZuulFilter zuulFilter = (ZuulFilter)list.get(i); Object result = this.processZuulFilter(zuulFilter); if (result != null && 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();
需要注意的是,ZuulServlet和ZuulServletFilter處理的是url為/zuul/*的請求,可以看List-4的this.zuulProperties.getServletPattern(),它的值就是/zuul,之后用ServletRegistration.Dynamic的addMapping方法加上處理的url為/zuul/*的。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。