1.Zuul1.X核心架构
在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请求生命周期
3.Zuul1.X执行流程
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
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
public class RequestContext extends ConcurrentHashMap {
private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class);
// 保存RequestContext自身类型
protected static Class extends RequestContext> contextClass = RequestContext.class;
private static RequestContext testContext = null;
// 静态final修饰的ThreadLocal实例,用于存放所有的RequestContext,每个RequestContext都会绑定在自身请求的处理线程中
// 注意这里的ThreadLocal实例的initialValue()方法,当ThreadLocal的get()方法返回null的时候总是会调用initialValue()方法
protected static final ThreadLocal extends RequestContext> 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!