RequestContext

Zuul 的上下文,继承 ConcurrentHashMap。内置 ThreadLocal 变量。因而每个线程都会有本人的 RequestContext 变量

ZuulFilter

ZuulFilter 实现 IZuulFilter Comparable 接口。

IZuulFilter 定义了 2 个办法

public interface IZuulFilter {    // 是否该拦挡    boolean shouldFilter();    // 拦挡之后做什么解决。注:如果返回值为非 boolean,不会做解决,这其实示意,该办法返回值 无意义    Object run() throws ZuulException;}

来看 ZuulFilter 中 2 个形象办法

abstract public String filterType();abstract public int filterOrder();

Zuul filterType 有以下几个值:prerouteposterror

  • pre

执行前拦挡

  • route

执行近程服务

  • post

执行后拦挡

  • error

产生谬误时拦挡

对于 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

ZuulProcessorZuul 的处理器。先看 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 时,才会做解决

ZuulServlet、ZuulServletFilter

Zuul 为了适配 HttpServlet,也因而有了 ZuulServletZuulServletFilter.
默认状况下,启用的是 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;    }}

ZuulServletZuulServletFilter 本质区别是:前者是基于 HttpServlet 实现,后者是基于 Filter 实现。从 j2ee 层面来说,Filter 的执行程序在 HttpServlet 之前

因为 ZuulServletZuulServletFilter 外围实现差不多,这里用 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();        }    }}

有几个细节解决一下:

  1. 因为 RequestConext 是基于 ThreadLocal 实现,因而在 finally 中调用 unset(), 而 unset() 办法,则是在外部调用了 ThreadLocal.remove()
  2. 如果未产生异样,则正确的解决流程为:pre -> route -> post
  3. pre 阶段产生异样

pre -> error -> post

  1. route 阶段产生异样

pre -> route -> error -> post

  1. post 阶段产生异样

pre -> route -> post -> error

  1. 在执行 pre, route, post, error 时,外部借助 zuulRunnerzuulRunner 由借助 FilterProcessor 调用对应的 runFilters("pre" || "route" ...)

SpringCloud 动静路由

动静路由定义:不仅仅能够从配置文件中加载路由,还能够从别的中央加载,例如数据库。当有新的路由增加后,须要能够动静的刷新路由

先看下 RouteLocator 接口

public interface RouteLocator {    Collection<String> getIgnoredPaths();    List<Route> getRoutes();    Route getMatchingRoute(String path);}

RouteLocator 的继承体系如下

其中 SimpleRouteLocator 不具备刷新性能,RefreshableRouteLocator 接口具备刷新性能。然而 SpringCloud 默认提供的实现类是 SimpleRouteLocator

这里再看,Zuul 启动后,如何加载路由

能够看到,是通过事件监听,从而刷新路由的。

动静路由食用 DEMO

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@SpringBootApplicationpublic 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";    }}

参考

zuul源码剖析-探索原生zuul的工作原理