⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 https://blog.csdn.net/haha7289/article/details/54344150 「haha7289」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

zuul支持动加载Filter类文件。实现原理是监控存放Filter文件的目录,定期扫描这些目录,如果发现有新Filter源码文件或者Filter源码文件有改动,则对文件进行编译加载。目前zuul支持使用Groovy编写的Filter。

FilterFileManager

FilterFileManager用于管理Filter存放目录,并定期扫描目录的变化。 FilterFileManager的类图 他的功能如下:

  • 开启一个线程,开始轮询

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>

FilterLoader类图

主要逻辑

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

是一个接口,定义两种加载编译源码的方法:

/**
方法最后返回的是Class,即源码编译成字节码后,还要加载。
需要支持热加载,文件变化更新等。
*/
Class compile(String sCode, String sName) throws Exception;

Class compile(File file) throws Exception;

总结

zuul整体框架比较简单,如果要实现API-Gateway,或者client-api adapter code,可以参考下。框架背后的思想也许更值得我们参考。

参考

666. 彩蛋

如果你对 Zuul 感兴趣,欢迎加入我的知识星球一起交流。

知识星球

文章目录
  1. 1. FilterFileManager
  2. 2. FilterLoader
  3. 3. FilterRegistry
  4. 4. DynamicCodeCompiler
  5. 5. 总结
  6. 6. 参考
  7. 7. 666. 彩蛋