本文讲述的是通过FilterFileManager实现zuul网关动态加载zuulFilter
zuul网关支持动态加载zuulFilter,原理是通过 FilterFileManager 监控 zuulFilter文件存放路径,定时扫描指定路径达到如发现 zuulFilter文件增加或修改,便进行编译加载。
源码 FilterFileManager 中需要注意的是
// 该方法中 pollingIntervalSeconds 为刷新频率 与方法startPoller()相关
//directories 为监控 zuulFilter文件存放路径 ,可多个。
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();
}
//该方法便是为了一个定时扫描的线程
void startPoller() {
this.poller = new Thread("GroovyFilterFileManagerPoller") {
public void run() {
while(FilterFileManager.this.bRunning) {
try {
sleep((long)(FilterFileManager.this.pollingIntervalSeconds * 1000));
FilterFileManager.this.manageFiles();
} catch (Exception var2) {
var2.printStackTrace();
}
}
}
};
this.poller.setDaemon(true);
this.poller.start();
}
// 该方法负责扫描加载
void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
List<File> aFiles = this.getFiles(); //负责扫描获取指定目录下所有 FilenameFilter.accept验证过的文件
this.processGroovyFiles(aFiles);//扫描文件中的 zuulFilter 文件并编译加载
}
//通过FilterLoader.putFilter()方法,检查每一个文件中 zuulFilter 是否需要编译加载
void processGroovyFiles(List<File> aFiles) throws Exception, InstantiationException, IllegalAccessException {
Iterator var2 = aFiles.iterator();
while(var2.hasNext()) {
File file = (File)var2.next();
FilterLoader.getInstance().putFilter(file);
}
}
源码 FilterLoader.putFilter
public boolean putFilter(File file) throws Exception {
String sName = file.getAbsolutePath() + file.getName();
if (this.filterClassLastModified.get(sName) != null && file.lastModified() != (Long)this.filterClassLastModified.get(sName)) {
LOG.debug("reloading filter " + sName);
this.filterRegistry.remove(sName);
}
ZuulFilter filter = this.filterRegistry.get(sName);
if (filter == null) {
Class clazz = COMPILER.compile(file); //获取文件中的zuulFilter
if (!Modifier.isAbstract(clazz.getModifiers())) {
filter = FILTER_FACTORY.newInstance(clazz);
List<ZuulFilter> list = (List)this.hashFiltersByType.get(filter.filterType());
if (list != null) {
this.hashFiltersByType.remove(filter.filterType());
}
this.filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);
this.filterClassLastModified.put(sName, file.lastModified());
return true;
}
}
return false;
}
本文介绍的并非是通过 GroovyCompiler(DynamicCodeCompiler) GroovyFileFilter(FilenameFilter) Groove代码方式实现动态加载zuulFilter,而是通过自定义FilenameFilter 和 DynamicCodeCompiler 实现
这两个自定义可以在项目启动后配置,即添加一个实现CommandLineRunner接口的bean,配置代码可以放在实现的run方法中
import com.netflix.zuul.FilterFileManager;
import com.netflix.zuul.FilterLoader;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class DynamicFilterRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
FilterLoader.getInstance().setCompiler(new DynamicZuulFilterCompiler());//加入自定义实例创建类
FilterFileManager.setFilenameFilter(new JarFileFilter());//加载文件过滤验证类
FilterFileManager.init(1," 文件加载路径");//载入刷新频率 和 文件加载路径
}
}
根据自己的需要创建实例
import com.netflix.zuul.DynamicCodeCompiler;
import java.io.File;
public class DynamicZuulFilterCompiler implements DynamicCodeCompiler {
@Override
public Class compile(String s, String s1) throws Exception {
return null;
}
@Override
public Class compile(File file) throws Exception {
/**
* 根据 file文件内容自定义获取并创建实例
*/
Class<?> aClass = Class.forName("com.TestZuulFilter");
return aClass;
}
}
文件加载路径下筛选文件的条件(例如下方是帅选出jar包文件)
import java.io.File;
import java.io.FilenameFilter;
/**
* @author peihua
*
* JavaFileFilter description: JAR格式验证
*
*/
public class JarFileFilter implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
// 验证文件是否已.jar文件结尾
return name.endsWith(".jar");
}
}