Zuul
的上下文,繼承 ConcurrentHashMap
。內置 ThreadLocal
變量。所以每一個線程都會有本身的 RequestContext
變量html
ZuulFilter
實現 IZuulFilter
Comparable
接口。java
IZuulFilter
定義了 2 個方法數據庫
public interface IZuulFilter { // 是否該攔截 boolean shouldFilter(); // 攔截以後作什麼處理。注:若是返回值爲非 boolean,不會作處理,這其實表示,該方法返回值 無心義 Object run() throws ZuulException; }
來看 ZuulFilter
中 2 個抽象方法app
abstract public String filterType(); abstract public int filterOrder();
Zuul
filterType
有如下幾個值:pre
、route
、post
、error
ide
執行前攔截源碼分析
執行遠程服務post
執行後攔截this
發生錯誤時攔截spa
關於 filterOrder()
先看 compareTo()
方法線程
public abstract class ZuulFilter implements Comparable<ZuulFilter> { // 升序 public int compareTo(ZuulFilter filter) { return Integer.compare(this.filterOrder(), filter.filterOrder()); } }
ZuulFilter
實現了 Comparable
接口。代表,每一個 ZuulFilter
之間是具有順序的,filterOrder()
方法返回的值 越小越靠前
再來看 runFilter()
方法
public ZuulFilterResult runFilter() { ZuulFilterResult zr = new ZuulFilterResult(); 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; }
對於每一個返回值,均被封裝爲 ZuulFiterResult
若是有異常,也不會被拋出,而是放入 ZuulFiterResult
中。
從 ZuulFilter 的實現原理來看,ZuulFilter 並不是是基於傳統的 Filter 實現。
ZuulProcessor
爲 Zuul
的處理器。先看 runFilters()
方法
public class 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; } }
public class FilterProcessor { public Object processZuulFilter(ZuulFilter filter) throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); boolean bDebug = ctx.debugRouting(); final String metricPrefix = "zuul.filter-"; long execTime = 0; 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); } break; default: break; } if (t != null) throw t; usageNotifier.notify(filter, s); return o; } catch (Throwable e) { if (bDebug) { Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + e.getMessage()); } usageNotifier.notify(filter, ExecutionStatus.FAILED); if (e instanceof ZuulException) { throw (ZuulException) e; } else { ZuulException ex = new ZuulException(e, "Filter threw Exception", 500, filter.filterType() + ":" + filterName); ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime); throw ex; } } } }
runFilters
方法遍歷全部的 ZuulFilters
並執行 processZuulFilter
方法. processZuulFilter
方法主要是執行了 ZuulFilter.runFilter
方法。
能夠看到,只有在當 ZuulFilter.runFilter
返回 Boolean
時,纔會作處理
Zuul
爲了適配 HttpServlet
,也所以有了 ZuulServlet
、ZuulServletFilter
.
默認狀況下,啓用的是 ZuulServlet
. 不過能夠經過 zuul.use-filter
值修改啓用類
public class ZuulServerAutoConfiguration { @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
、ZuulServletFilter
本質區別是:前者是基於 HttpServlet
實現,後者是基於 Filter
實現。從 j2ee
層面來講,Filter
的執行順序在 HttpServlet
以前
由於 ZuulServlet
、ZuulServletFilter
核心實現差很少,這裏用 ZuulServlet
來講明
public class ZuulServlet extends HttpServlet { @Override public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException { try { init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse); 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(); } } }
有幾個細節處理一下:
RequestConext
是基於 ThreadLocal 實現,所以在 finally
中調用 unset()
, 而 unset()
方法,則是在內部調用了 ThreadLocal.remove()
pre
-> route
-> post
pre
-> error
-> post
pre
-> route
-> error
-> post
pre
-> route
-> post
-> error
pre
, route
, post
, error
時,內部藉助 zuulRunner
,zuulRunner
由藉助 FilterProcessor
調用對應的 runFilters("pre" || "route" ...)
動態路由定義:不單單能夠從配置文件中加載路由,還能夠從別的地方加載,例如數據庫。當有新的路由添加後,須要能夠動態的刷新路由
先看下 RouteLocator
接口
public interface RouteLocator { Collection<String> getIgnoredPaths(); List<Route> getRoutes(); Route getMatchingRoute(String path); }
RouteLocator
的繼承體系以下
其中 SimpleRouteLocator
不具有刷新功能,RefreshableRouteLocator
接口具有刷新功能。可是 SpringCloud 默認提供的實現類是 SimpleRouteLocator
這裏再看,Zuul
啓動後,如何加載路由
能夠看到,是經過事件監聽,從而刷新路由的。
public class MyRouteLocator extends SimpleRouteLocator implements RefreshableRouteLocator { public MyRouteLocator(String servletPath, ZuulProperties properties) { super(servletPath, properties); } @Override public void refresh() { super.doRefresh(); } protected Map<String, ZuulRoute> locateRoutes() { Map<String, ZuulRoute> routesMap = super.locateRoutes(); // TODO 實現你本身的加載邏輯 return routesMap; } } @RestController @EnableZuulProxy @SpringBootApplication public class ZuulApplication { @Lazy @Resource MyRouteLocator myRouteLocator; @Autowired ApplicationContext applicationContext; @GetMapping("/test") public Object test() { // 從源碼流程看,咱們須要發佈刷新事件。由於發佈刷新事件,dirty 會被設置爲 true, 設置爲 true 後,纔會從新註冊對應 URL 的 HandlerMapping applicationContext.publishEvent(new RoutesRefreshedEvent(myRouteLocator)); return "12"; } }