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(List<File> aFiles) {
for (File file : aFiles) {
FilterLoader.getInstance().putFilter(file);
}
}
void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
List<File> aFiles = getFiles();
processGroovyFiles(aFiles);
}
FilterLoader
编译、加载filter文件,并且检查源文件是否有变更,除此之外,它还按照filterType
组织并维护List<ZuulFilter>
主要逻辑
代码语言:javascript复制 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<Filter>缓存,下次Request重新构建
List<ZuulFilter> 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
是一个接口,定义两种加载编译源码的方法:
代码语言:javascript复制 /**
方法最后返回的是Class,即源码编译成字节码后,还要加载。
需要支持热加载,文件变化更新等。
*/
Class compile(String sCode, String sName) throws Exception;
Class compile(File file) throws Exception;
总结
zuul整体框架比较简单,如果要实现API-Gateway,或者client-api adapter code,可以参考下。框架背后的思想也许更值得我们参考。
参考
- Announcing Zuul: Edge Service in the Cloud
- Building Microservices: Using an API Gateway
- Embracing the Differences : Inside the Netflix API Redesign
- Pattern: API Gateway
- Optimizing the Netflix API