zuul支持动加载Filter类文件。实现原理是监控存放Filter文件的目录,定期扫描这些目录,如果发现有新Filter源码文件或者Filter源码文件有改动,则对文件进行编译加载。目前zuul支持使用Groovy编写的Filter。
FilterFileManager
FilterFileManager
用于管理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(); }
每次轮询,处理目录内的所有*.groovy文件,即调用FilterLoader.getInstance().putFilter(file);
void processGroovyFiles(ListaFiles) { for (File file : aFiles) { FilterLoader.getInstance().putFilter(file); } } void manageFiles() throws Exception, IllegalAccessException, InstantiationException { List aFiles = getFiles(); processGroovyFiles(aFiles); }
FilterLoader
编译、加载filter文件,并且检查源文件是否有变更,除此之外,它还按照filterType
组织并维护List<ZuulFilter>
主要逻辑
public boolean putFilter(File file) throws Exception { String sName = file.getAbsolutePath() + file.getName(); // 如果文件在上次加载后发生了变化,重新编译加载 if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(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); // 为了下次Request使用filter,清空filter.filterType()类型的List缓存,下次Request重新构建 List list = hashFiltersByType.get(filter.filterType()); if (list != null) { hashFiltersByType.remove(filter.filterType()); //rebuild this list } // filterRegistry 管理所有的filter, filterRegistry.put(file.getAbsolutePath() + file.getName(), filter); // 记录filter文件更新时间。 filterClassLastModified.put(sName, file.lastModified()); return true; } } return false; }
这里主要的逻辑是把Groovy源码进行编译并加载进jvm里。
FilterRegistry
用于管理加载的filter,比较简单,使用
ConcurrentHashMap<String, ZuulFilter> filters
,启动key为filter的name:file.getAbsolutePath() + file.getName();
DynamicCodeCompiler
是一个接口,定义两种加载编译源码的方法:
/** 方法最后返回的是Class,即源码编译成字节码后,还要加载。 需要支持热加载,文件变化更新等。 */ Class compile(String sCode, String sName) throws Exception; Class compile(File file) throws Exception;
总结
zuul整体框架比较简单,如果要实现API-Gateway,或者client-api adapter code,可以参考下。框架背后的思想也许更值得我们参考。