(一)深入浅出之Zuul1.X源码解析

1.Zuul1.X核心架构

Zuul1.X核心架构.png

在Zuul网关中, 整个请求的过程如图所示,首先将用户请求给ZuulServlet处理,ZuulServlet中有一个ZuulRunner类对象,该类对象中初始化了RequestContext:作为存储整个请求的一些数据,并被所有的ZuulFilter共享。ZuulRunner中还有 FilterProcessor,FilterProcessor作为执行所有的ZuulFilter的管理器。FilterProcessor从FilterLoader 中获取Zuulfilter,而Zuulfilter是被FilterFileManager所加载,并支持groovy热加载,采用了轮询的方式热加载。有了这些filter之后,ZuulServelet首先执行pre类型的过滤器,再执行route类型的过滤器,最后执行的是post 类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器。执行完这些过滤器,最终将请求的结果返回给客户端。
2.Zuul1.X请求生命周期

Zuul1.X请求生命周期.png

3.Zuul1.X执行流程

Zuul请求执行流程.png

4.Zuul1.X核心类源码解析
1)ZuulServlet源码解析
ZuulServlet:核心Zuul Servlet,用于初始化和协调ZuulFilter的执行。

/**
 * Core Zuul servlet which intializes and orchestrates zuulFilter execution
 *
 * @author Mikey Cohen
 *         Date: 12/23/11
 *         Time: 10:44 AM
 */
public class ZuulServlet extends HttpServlet {

    private static final long serialVersionUID = -3374242278843351500L;
    // Zuul执行器:将Servlet请求和响应初始化到RequestContext中,并将FilterProcessor调用包装为preRoute(),route(),postRoute()和error()方法。
    private ZuulRunner zuulRunner;

    /**
     * 初始化ZuulRunner
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化ZuulServlet
        super.init(config);
        // 是否缓存请求标识
        String bufferReqsStr = config.getInitParameter("buffer-requests");
        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true") ? true : false;
        // 创建ZuulRunner对象
        zuulRunner = new ZuulRunner(bufferReqs);
    }

    /**
     * 处理请求的核心方法
     *
     * @param servletRequest 请求对象
     * @param servletResponse 响应对象
     * @throws ServletException
     * @throws IOException
     */
    @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            // 初始化请求,内部调用zuulRunner.init(servletRequest, servletResponse)方法将servletRequest请求和servletResponse响应设置到RequestContext中
            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();
            // 表示zuul已经在执行
            context.setZuulEngineRan();

            try {
                // 执行前置过滤器
                preRoute();
            } catch (ZuulException e) {
                // pre异常: pre -> error -> post
                error(e);
                postRoute();
                return;
            }
            try {
                // 执行路由过滤器
                route();
            } catch (ZuulException e) {
                // route异常: pre->route -> error -> post
                error(e);
                postRoute();
                return;
            }
            try {
                // 执行后置过滤器
                postRoute();
            } catch (ZuulException e) {
                // post异常: pre->route -> post-> error
                error(e);
                return;
            }
        // 正常: pre -> route -> post
        } catch (Throwable e) {
            // 执行错误过滤器
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

    /**
     * executes "post" ZuulFilters
     *
     * @throws ZuulException
     */
    void postRoute() throws ZuulException {
        zuulRunner.postRoute();
    }

    /**
     * executes "route" filters
     *
     * @throws ZuulException
     */
    void route() throws ZuulException {
        zuulRunner.route();
    }

    /**
     * executes "pre" filters
     *
     * @throws ZuulException
     */
    void preRoute() throws ZuulException {
        zuulRunner.preRoute();
    }

    /**
     * initializes request
     *
     * @param servletRequest
     * @param servletResponse
     */
    void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
        zuulRunner.init(servletRequest, servletResponse);
    }

    /**
     * sets error context info and executes "error" filters
     *
     * @param e
     */
    void error(ZuulException e) {
        RequestContext.getCurrentContext().setThrowable(e);
        zuulRunner.error();
    }
}

跟踪init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse),可以发现这个方法为每个请求生成RequestContext,RequestContext继承了ConcurrentHashMap,在请求结束时销毁掉该RequestContext,RequestContext的生命周期为请求到zuulServlet开始处理,直到请求结束返回结果。

2)ZuulRunner源码解析
ZuulRunner:将Servlet请求和响应初始化到RequestContext中,并将FilterProcessor的调用包装为preRoute(),route(),postRoute()和error()方法。

/**
 * This class initializes servlet requests and responses into the RequestContext and wraps the FilterProcessor calls
 * to preRoute(), route(),  postRoute(), and error() methods
 *
 * @author [email protected]
 * @version 1.0
 */
public class ZuulRunner {

    private boolean bufferRequests;

    /**
     * Creates a new ZuulRunner instance.
     */
    public ZuulRunner() {
        this.bufferRequests = true;
    }

    /**
     *
     * @param bufferRequests - whether to wrap the ServletRequest in HttpServletRequestWrapper and buffer the body.
     */
    public ZuulRunner(boolean bufferRequests) {
        this.bufferRequests = bufferRequests;
    }

    /**
     * sets HttpServlet request and HttpResponse
     * 将servletRequest请求和servletResponse响应设置到RequestContext中供ZuulFilters访问和共享数据
     * @param servletRequest
     * @param servletResponse
     */
    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
        if (bufferRequests) {
            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
        } else {
            ctx.setRequest(servletRequest);
        }

        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));
    }

    /**
     * executes "post" filterType  ZuulFilters
     * 包装FilterProcessor执行post类型过滤器
     * @throws ZuulException
     */
    public void postRoute() throws ZuulException {
        FilterProcessor.getInstance().postRoute();
    }

    /**
     * executes "route" filterType  ZuulFilters
     * 包装FilterProcessor执行route类型过滤器
     * @throws ZuulException
     */
    public void route() throws ZuulException {
        FilterProcessor.getInstance().route();
    }

    /**
     * executes "pre" filterType  ZuulFilters
     * 包装FilterProcessor执行pre类型过滤器
     * @throws ZuulException
     */
    public void preRoute() throws ZuulException {
        FilterProcessor.getInstance().preRoute();
    }

    /**
     * executes "error" filterType  ZuulFilters
     * 包装FilterProcessor执行error类型过滤器
     */
    public void error() {
        FilterProcessor.getInstance().error();
    }
}

3)RequestContext源码解析
RequestContext:RequestContext(请求上下文)保存请求、响应、状态信息和数据供ZuulFilters访问和共享。RequestContext在整个请求期间有效,并且是线程本地的。
注意:每一个新的请求都是由一个独立的线程处理(这个线程是Tomcat里面起的线程),换言之,请求的所有参数(Http报文信息解析出来的内容,如请求头、请求体等等)总是绑定在处理请求的线程中。RequestContext继承于ConcurrentHashMap,参数可以直接设置在RequestContext中。我们在此仅分析RequestContext的初始化流程的源码。

public class RequestContext extends ConcurrentHashMap {

    private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class);
    // 保存RequestContext自身类型
    protected static Class contextClass = RequestContext.class;

    private static RequestContext testContext = null;
    // 静态final修饰的ThreadLocal实例,用于存放所有的RequestContext,每个RequestContext都会绑定在自身请求的处理线程中
    // 注意这里的ThreadLocal实例的initialValue()方法,当ThreadLocal的get()方法返回null的时候总是会调用initialValue()方法
    protected static final ThreadLocal threadLocal = new ThreadLocal() {
        @Override
        protected RequestContext initialValue() {
            try {
                return contextClass.newInstance();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    };
  
    public RequestContext() {
        super();
    }
    /**
     * Get the current RequestContext
     *
     * @return the current RequestContext
     */
    public static RequestContext getCurrentContext() {
        if (testContext != null) return testContext;
       // 当ThreadLocal的get()方法返回null的时候总是会调用initialValue()方法,所以这里是"无RequestContext 则新建RequestContext"的逻辑
        RequestContext context = threadLocal.get();
        return context;
    }
}

4)FilterProcessor源码解析
FilterProcessor:执行filter过滤器的核心类。

/**
 * This the the core class to execute filters.
 *
 * @author Mikey Cohen
 *         Date: 10/24/11
 *         Time: 12:47 PM
 */
public class FilterProcessor {
    // FilterProcessor单例:饿汉模式
    static FilterProcessor INSTANCE = new FilterProcessor();
    protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);
    // Filter使用计数器
    private FilterUsageNotifier usageNotifier;

    public FilterProcessor() {
        usageNotifier = new BasicFilterUsageNotifier();
    }

    /**
     * @return the singleton FilterProcessor
     */
    public static FilterProcessor getInstance() {
        return INSTANCE;
    }

    /**
     * sets a singleton processor in case of a need to override default behavior
     *
     * @param processor
     */
    public static void setProcessor(FilterProcessor processor) {
        INSTANCE = processor;
    }

    /**
     * Override the default filter usage notification impl.
     *
     * @param notifier
     */
    public void setFilterUsageNotifier(FilterUsageNotifier notifier) {
        this.usageNotifier = notifier;
    }

    /**
     * runs "post" filters which are called after "route" filters. ZuulExceptions from ZuulFilters are thrown.
     * Any other Throwables are caught and a ZuulException is thrown out with a 500 status code
     *
     * @throws ZuulException
     */
    public void postRoute() throws ZuulException {
        try {
            runFilters("post");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
        }
    }

    /**
     * runs all "error" filters. These are called only if an exception occurs. Exceptions from this are swallowed and logged so as not to bubble up.
     */
    public void error() {
        try {
            runFilters("error");
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
    }

    /**
     * Runs all "route" filters. These filters route calls to an origin.
     *
     * @throws ZuulException if an exception occurs.
     */
    public void route() throws ZuulException {
        try {
            runFilters("route");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + e.getClass().getName());
        }
    }

    /**
     * runs all "pre" filters. These filters are run before routing to the orgin.
     *
     * @throws ZuulException
     */
    public void preRoute() throws ZuulException {
        try {
            runFilters("pre");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
        }
    }

    /**
     * runs all filters of the filterType sType/ Use this method within filters to run custom filters by type
     * 运行过滤器类型为sType的的所有过滤器/使用此方法运行自定义的过滤器类型
     * @param sType the filterType(过滤器类型:pre,route,post,error)
     * @return
     * @throws Throwable throws up an arbitrary exception
     */
    public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        //根据具体的过滤器类型返回ZuulFilters过滤器列表(按优先级排序)
        List list = FilterLoader.getInstance().getFiltersByType(sType);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                ZuulFilter zuulFilter = list.get(i);
                // 处理单个ZuulFilter。此方法添加调试信息。任何未被捕获的thowable都会被此方法捕获并转换为具有500状态代码的ZuulException。
                Object result = processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= ((Boolean) result);
                }
            }
        }
        return bResult;
    }

    /**
     * Processes an individual ZuulFilter. This method adds Debug information. Any uncaught Thowables are caught by this method and converted to a ZuulException with a 500 status code.
     * 处理单个ZuulFilter。此方法添加调试信息。任何未被捕获的thowable都会被此方法捕获并转换为具有500状态代码的ZuulException。
     * @param filter
     * @return the return value for that filter(返回此过滤器的值)
     * @throws ZuulException
     */
    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);
                // RequestContext副本用于debugging
                copy = ctx.copy();
            }
            // 调用ZuulFilter的runFilter()方法执行过滤器
            ZuulFilterResult result = filter.runFilter();
            // 获取执行状态
            ExecutionStatus s = result.getStatus();
            execTime = System.currentTimeMillis() - ltime;

            switch (s) {
                // 执行失败
                case FAILED:
                    t = result.getException();
                    // 在RequestContext中添加过滤器的执行结果
                    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");
                        // 用于记录过滤器是否添加了新的key到requestContext对象中
                        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;
            }
        }
    }

    /**
     * Publishes a counter metric for each filter on each use.
     */
    public static class BasicFilterUsageNotifier implements FilterUsageNotifier {
        private static final String METRIC_PREFIX = "zuul.filter-";

        @Override
        public void notify(ZuulFilter filter, ExecutionStatus status) {
            DynamicCounter.increment(METRIC_PREFIX + filter.getClass().getSimpleName(), "status", status.name(), "filtertype", filter.filterType());
        }
    }
}

5)ZuulFilter源码解析
ZuulFilter:ZuulFilters的基本抽象类。定义的抽象方法有如下两个:
① filterType():按过滤器类型对过滤器进行分类。Zuul中的标准类型是“pre”用于前置路由过滤,“route”用于路由到源服务,“post”用于后置路由过滤,“error”用于错误处理。我们还支持“static”类型用于静态响应请参阅StaticResponseFilter。通过调用FilterProcessor.runFilters(type)创建或添加并运行任何filterType。
② filterOrder():定义过滤器的执行优先级,filterOrders不需要是顺序的,数值越小优先级越高。

package com.netflix.zuul;

import com.netflix.zuul.exception.ZuulException;

/**
 * BAse interface for ZuulFilters
 *
 * @author Mikey Cohen
 *         Date: 10/27/11
 *         Time: 3:03 PM
 */
public interface IZuulFilter {
    /**
     * a "true" return from this method means that the run() method should be invoked
     *
     * @return true if the run() method should be invoked. false will not invoke the run() method
     */
    boolean shouldFilter();

    /**
     * if shouldFilter() is true, this method will be invoked. this method is the core method of a ZuulFilter
     *
     * @return Some arbitrary artifact may be returned. Current implementation ignores it.
     * @throws ZuulException if an error occurs during execution.
     */
    Object run() throws ZuulException;

}
/**
 * Base abstract class for ZuulFilters. The base class defines abstract methods to define:
 * filterType() - to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
 * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
 * We also support a "static" type for static responses see  StaticResponseFilter.
 * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type)
 * 

* filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not * important for a filter. filterOrders do not need to be sequential. *

* ZuulFilters may be disabled using Archius Properties. *

* By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false * * @author Mikey Cohen * Date: 10/26/11 * Time: 4:29 PM */ public abstract class ZuulFilter implements IZuulFilter, Comparable { // AtomicReference类用于保证引用对象之间的原子性以方便CAS操作 private final AtomicReference filterDisabledRef = new AtomicReference<>(); /** * to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering, * "route" for routing to an origin, "post" for post-routing filters, "error" for error handling. * We also support a "static" type for static responses see StaticResponseFilter. * Any filterType made be created or added and run by calling FilterProcessor.runFilters(type) * * @return A String representing that type */ abstract public String filterType(); /** * filterOrder() must also be defined for a filter. Filters may have the same filterOrder if precedence is not * important for a filter. filterOrders do not need to be sequential. * * @return the int order of a filter */ abstract public int filterOrder(); /** * By default ZuulFilters are static; they don't carry state. This may be overridden by overriding the isStaticFilter() property to false * * @return true by default */ public boolean isStaticFilter() { return true; } /** * The name of the Archaius property to disable this filter. by default it is zuul.[classname].[filtertype].disable * * @return */ public String disablePropertyName() { return "zuul." + this.getClass().getSimpleName() + "." + filterType() + ".disable"; } /** * If true, the filter has been disabled by archaius and will not be run * 判断filter是否被禁用(可以使用Archius属性禁用ZuulFilters) * @return */ public boolean isFilterDisabled() { filterDisabledRef.compareAndSet(null, DynamicPropertyFactory.getInstance().getBooleanProperty(disablePropertyName(), false)); return filterDisabledRef.get().get(); } /** * runFilter checks !isFilterDisabled() and shouldFilter(). The run() method is invoked if both are true. * * @return the return from ZuulFilterResult */ public ZuulFilterResult runFilter() { ZuulFilterResult zr = new ZuulFilterResult(); if (!isFilterDisabled()) { // 判断filter是否被禁用 if (shouldFilter()) { // 判断是否进行过滤 Tracer t = TracerFactory.instance().startMicroTracer("ZUUL::" + this.getClass().getSimpleName()); try { // run()方法是ZuulFilter的核心方法,需重写run()方法实现自定义响应内容 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; } public int compareTo(ZuulFilter filter) { return Integer.compare(this.filterOrder(), filter.filterOrder()); } }

6)FilterLoader源码解析
FilterLoader:是Zuul的核心类之一。 它从文件中编译、加载和检查源代码是否被修改,并按过滤器类型(filterType)保存ZuulFilters。

/**
 * This class is one of the core classes in Zuul. It compiles, loads from a File, and checks if source code changed.
 * It also holds ZuulFilters by filterType.
 *
 * @author Mikey Cohen
 *         Date: 11/3/11
 *         Time: 1:59 PM
 */
public class FilterLoader {
    // 单例——饿汉模式
    final static FilterLoader INSTANCE = new FilterLoader();

    private static final Logger LOG = LoggerFactory.getLogger(FilterLoader.class);
    // 缓存Filter名称(主要是从文件加载,名称为绝对路径 + 文件名的形式)->Filter最后修改时间戳的映射
    private final ConcurrentHashMap filterClassLastModified = new ConcurrentHashMap();
    // 缓存Filter名字->Filter代码的映射,实际上这个Map只使用到get方法进行存在性判断,一直是一个空的结构
    private final ConcurrentHashMap filterClassCode = new ConcurrentHashMap();
     // 缓存Filter名字->Filter名字的映射,用于存在性判断
    private final ConcurrentHashMap filterCheck = new ConcurrentHashMap();
    // 缓存Filter类型名称->List的映射
    private final ConcurrentHashMap> hashFiltersByType = new ConcurrentHashMap>();
    // ZuulFilter全局缓存的单例
    private FilterRegistry filterRegistry = FilterRegistry.instance();
    // 动态代码编译器实例,Zuul提供的默认实现是GroovyCompiler
    static DynamicCodeCompiler COMPILER;
    // ZuulFilter的工厂类
    static FilterFactory FILTER_FACTORY = new DefaultFilterFactory();

    /**
     * Sets a Dynamic Code Compiler
     *
     * @param compiler
     */
    public void setCompiler(DynamicCodeCompiler compiler) {
        COMPILER = compiler;
    }

    // overidden by tests
    public void setFilterRegistry(FilterRegistry r) {
        this.filterRegistry = r;
    }

    /**
     * Sets a FilterFactory
     * 
     * @param factory
     */
    public void setFilterFactory(FilterFactory factory) {
        FILTER_FACTORY = factory;
    }
    
    /**
     * @return Singleton FilterLoader
     */
    public static FilterLoader getInstance() {
        return INSTANCE;
    }

    /**
     * Given source and name will compile and store the filter if it detects that the filter code has changed or
     * the filter doesn't exist. Otherwise it will return an instance of the requested ZuulFilter
     * 通过ZuulFilter的类代码和Filter名称获取ZuulFilter实例
     * @param sCode source code
     * @param sName name of the filter
     * @return the ZuulFilter
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public ZuulFilter getFilter(String sCode, String sName) throws Exception {
        // 检查filterCheck是否存在相同名字的Filter,如果存在说明已经加载过
        if (filterCheck.get(sName) == null) {
            // filterCheck中放入Filter名称
            filterCheck.putIfAbsent(sName, sName);
            // filterClassCode中不存在加载过的Filter名称对应的代码
            if (!sCode.equals(filterClassCode.get(sName))) {
                LOG.info("reloading code " + sName);
                // 从全局缓存中移除对应的Filter
                filterRegistry.remove(sName);
            }
        }
        ZuulFilter filter = filterRegistry.get(sName);
        // 如果全局缓存中不存在对应的Filter,就使用DynamicCodeCompiler加载代码,使用FilterFactory实例化ZuulFilter
        // 注意加载的ZuulFilter类不能是抽象的,必须是继承了ZuulFilter的子类
        if (filter == null) {
            Class clazz = COMPILER.compile(sCode, sName);
            if (!Modifier.isAbstract(clazz.getModifiers())) {
                filter = (ZuulFilter) FILTER_FACTORY.newInstance(clazz);
            }
        }
        return filter;

    }

    /**
     * @return the total number of Zuul filters
     *  返回所有缓存的ZuulFilter实例的总数量
     */
    public int filterInstanceMapSize() {
        return filterRegistry.size();
    }


    /**
     * From a file this will read the ZuulFilter source code, compile it, and add it to the list of current filters
     * a true response means that it was successful.
     * 通过文件加加载ZuulFilter
     * @param file
     * @return true if the filter in file successfully read, compiled, verified and added to Zuul
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IOException
     */
    public boolean putFilter(File file) throws Exception {
        // Filter名称为文件的绝对路径+文件名
        String sName = file.getAbsolutePath() + file.getName();
        // 如果文件被修改过则从全局缓存从移除对应的Filter以便重新加载
        if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) {
            LOG.debug("reloading filter " + sName);
            filterRegistry.remove(sName);
        }
        ZuulFilter filter = filterRegistry.get(sName);
        if (filter == null) {
            Class clazz = COMPILER.compile(file);
            if (!Modifier.isAbstract(clazz.getModifiers())) {
                filter = (ZuulFilter) FILTER_FACTORY.newInstance(clazz);
                List list = hashFiltersByType.get(filter.filterType());
                //这里说明了一旦文件有修改,hashFiltersByType中对应的当前文件加载出来的Filter类型的缓存要移除,原因见getFiltersByType(String filterType)方法
                if (list != null) {
                    hashFiltersByType.remove(filter.filterType()); //rebuild this list
                }
                filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
                filterClassLastModified.put(sName, file.lastModified());
                return true;
            }
        }

        return false;
    }

    /**
     * Returns a list of filters by the filterType specified
     * 通过Filter类型获取同类型的所有ZuulFilter
     * @param filterType
     * @return a List
     */
    public List getFiltersByType(String filterType) {

        List list = hashFiltersByType.get(filterType);
        if (list != null) return list;

        list = new ArrayList();
        // 如果hashFiltersByType缓存被移除,这里从全局缓存中加载所有的ZuulFilter,按照指定类型构建一个新的列表
        Collection filters = filterRegistry.getAllFilters();
        for (Iterator iterator = filters.iterator(); iterator.hasNext(); ) {
            ZuulFilter filter = iterator.next();
            if (filter.filterType().equals(filterType)) {
                list.add(filter);
            }
        }
        // 基于filterOrder排序
        Collections.sort(list); // sort by priority
        // 这里总是putIfAbsent,这就是为什么上个方法可以放心地在修改的情况下移除指定Filter类型中的全部缓存实例的原因
        hashFiltersByType.putIfAbsent(filterType, list);
        return list;
    }
}

7)FilterRegistry源码解析
FilterRegistry是Filter的注册中心,其核心内容是维护了一个ZuulFilter的内容列表

public class FilterRegistry {
    // 饿汉式单例,确保全局只有一个ZuulFilter的缓存
    private static final FilterRegistry INSTANCE = new FilterRegistry();

    public static final FilterRegistry instance() {
        return INSTANCE;
    }
    // FilterRegistry核心代码:维护一个ZuulFilter集合
    // 缓存字符串到ZuulFilter实例的映射关系,如果是从文件加载,字符串key的格式是:文件绝对路径 + 文件名,当然也可以自实现
    private final ConcurrentHashMap filters = new ConcurrentHashMap();

    private FilterRegistry() {
    }

    public ZuulFilter remove(String key) {
        return this.filters.remove(key);
    }

    public ZuulFilter get(String key) {
        return this.filters.get(key);
    }

    public void put(String key, ZuulFilter filter) {
        this.filters.putIfAbsent(key, filter);
    }

    public int size() {
        return this.filters.size();
    }

    public Collection getAllFilters() {
        return this.filters.values();
    }

}

8)FilterFileManager源码解析
FilterFileManager:管理目录轮询以查找更改和新的Groovy过滤器(Groovy Filter)。轮询间隔和目录在类的初始化中指定,轮询器将检查更改和添加。

/**
 * This class manages the directory polling for changes and new Groovy filters.
 * Polling interval and directories are specified in the initialization of the class, and a poller will check
 * for changes and additions.
 *
 * @author Mikey Cohen
 *         Date: 12/7/11
 *         Time: 12:09 PM
 */
public class FilterFileManager {

    private static final Logger LOG = LoggerFactory.getLogger(FilterFileManager.class);

    String[] aDirectories;
    int pollingIntervalSeconds;
    Thread poller;
    boolean bRunning = true;
    // 文件名过滤器,Zuul中的默认实现是GroovyFileFilter,只接受.groovy后缀的文件
    static FilenameFilter FILENAME_FILTER;

    static FilterFileManager INSTANCE;

    private FilterFileManager() {
    }

    public static void setFilenameFilter(FilenameFilter filter) {
        FILENAME_FILTER = filter;
    }

    /**
     * Initialized the GroovyFileManager.
     * init方法是核心静态方法,它具备了配置,预处理和激活后台轮询线程的功能
     * @param pollingIntervalSeconds the polling interval in Seconds
     * @param directories            Any number of paths to directories to be polled may be specified
     * @throws IOException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public static void init(int pollingIntervalSeconds, String... directories) throws Exception, IllegalAccessException, InstantiationException {
        if (INSTANCE == null) INSTANCE = new FilterFileManager();

        INSTANCE.aDirectories = directories;
        INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds;
        INSTANCE.manageFiles();
        INSTANCE.startPoller();

    }

    public static FilterFileManager getInstance() {
        return INSTANCE;
    }

    /**
     * Shuts down the poller
     */
    public static void shutdown() {
        INSTANCE.stopPoller();
    }


    void stopPoller() {
        bRunning = false;
    }
   // 启动后台轮询守护线程,每休眠pollingIntervalSeconds秒则进行一次文件扫描尝试更新Filter
    void startPoller() {
        poller = new Thread("GroovyFilterFileManagerPoller") {
            public void run() {
                while (bRunning) {
                    try {
                        sleep(pollingIntervalSeconds * 1000);
                        manageFiles();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        };
       // 设置为守护线程
        poller.setDaemon(true);
        poller.start();
    }

    /**
     * Returns the directory File for a path. A Runtime Exception is thrown if the directory is in valid
     * 根据指定目录路径获取目录,主要需要转换为ClassPath
     * @param sPath
     * @return a File representing the directory path
     */
    public File getDirectory(String sPath) {
        File  directory = new File(sPath);
        if (!directory.isDirectory()) {
            URL resource = FilterFileManager.class.getClassLoader().getResource(sPath);
            try {
                directory = new File(resource.toURI());
            } catch (Exception e) {
                LOG.error("Error accessing directory in classloader. path=" + sPath, e);
            }
            if (!directory.isDirectory()) {
                throw new RuntimeException(directory.getAbsolutePath() + " is not a valid directory");
            }
        }
        return directory;
    }

    /**
     * Returns a List of all Files from all polled directories
     * 遍历配置目录,获取所有配置目录下的所有满足FileNameFilter过滤条件的文件
     * @return
     */
    List getFiles() {
        List list = new ArrayList();
        for (String sDirectory : aDirectories) {
            if (sDirectory != null) {
                File directory = getDirectory(sDirectory);
                File[] aFiles = directory.listFiles(FILENAME_FILTER);
                if (aFiles != null) {
                    list.addAll(Arrays.asList(aFiles));
                }
            }
        }
        return list;
    }

    /**
     * puts files into the FilterLoader. The FilterLoader will only addd new or changed filters
     * 遍历指定文件列表,调用FilterLoader单例中的putFilter
     * @param aFiles a List
     * @throws IOException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    void processGroovyFiles(List aFiles) throws Exception, InstantiationException, IllegalAccessException {

        for (File file : aFiles) {
            FilterLoader.getInstance().putFilter(file);
        }
    }
    // 获取指定目录下的所有文件,调用processGroovyFiles
    void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
        List aFiles = getFiles();
        processGroovyFiles(aFiles);
    }
}

原创不易,如需转载,请注明出处@author Davince!

你可能感兴趣的:((一)深入浅出之Zuul1.X源码解析)