Zuul 2是如何动态加载Filter的?

article/2025/9/22 6:46:24

Zuul 2沿用了Zuul 1的责任链模式的设计,其网关核心功能还是通过Filter链来实现的。要熟练使用和扩展Zuul 2的功能,必须要了解其Filter的加载和执行机制。另外,Zuul 2使用Guice作为依赖注入工具,因此在开始分析之前,我们需要大致了解Guice的基本原理和用法,传送门:Guide to Google Guice

为了了解Zuul 2的filter加载机制,我们从入口开始看起。在官方提供的zuul-sample项目中,启动类是Bootstrap,在其start方法中,可以看到这么几行:

ConfigurationManager.loadCascadedPropertiesFromResources("application");
Injector injector = InjectorBuilder.fromModule(new ZuulSampleModule()).createInjector();
BaseServerStartup serverStartup = injector.getInstance(BaseServerStartup.class);
server = serverStartup.server();

在上述代码中使用了ConfigurationManager去加载application.properties配置文件,然后通过Guice创建ZuulSampleModule,接着创建BaseServerStartup和Server实例。
在ZuulSampleModule的configure方法中,跟filter加载相关的是如下两行:

install(new ZuulFiltersModule());
bind(FilterFileManager.class).asEagerSingleton();

在ZuulFiltersModule的configure方法中,主要做的事情是指定了GroovyCompiler、GuiceFilterFactory和BasicFilterUsageNotifier的Guice注入绑定。而provideFilterFileManagerConfig方法,则是根据配置文件中zuul.filters.locations属性按照路径加载filterLocations,以及根据zuul.filters.packageszuul.filters.classes属性按照类名加载filterClassNames,然后据此创建FilterFileManagerConfig对象并返回。

FilterFileManagerConfig对象的属性如下:

private String[] directories; // filter文件夹路径
private String[] classNames; // filter类名
private int pollingIntervalSeconds; // 扫描时间间隔,秒数
private FilenameFilter filenameFilter; // 文件名过滤器

FilterFileManager会根据FilterFileManagerConfig指定的配置,使用FilterLoader执行filter文件加载,同样地这两个实例也是通过Guice注入到FilterFileManager的构造器中。
FilterLoader的类图如下:
在这里插入图片描述

FilterLoader其主要功能是按照类名、文件名、脚本内容等方式使用DynamicCodeCompiler去加载为ZuulFilter实例,为了只加载新增或者变更了的filter文件,其内部使用了一些ConcurrentMap记住已经加载过的filter及其上次修改时间戳。FilterRegistry是filter注册表,其类图如下:
在这里插入图片描述

在FilterFileManager的构造器中,除了设置相关属性以外,还启动了名为processFilesService的固定大小线程池,主要是为了能够异步地使用filterLoader去动态加载filter文件。

在FilterFileManager的@PostConstruct方法init中,主要做了三件事情:

  • 加载config中classNames属性直接指定的filter;
  • 加载config中directories属性指定的路径下的filter;
  • 启动一个名为poller的Thread去定时检测config中directories属性指定的路径下的filter文件是否有更新;
    其代码如下:
    /*** Initialized the GroovyFileManager.** @throws Exception*/@PostConstructpublic void init() throws Exception{long startTime = System.currentTimeMillis();filterLoader.putFiltersForClasses(config.getClassNames());manageFiles();startPoller();LOG.warn("Finished loading all zuul filters. Duration = " + (System.currentTimeMillis() - startTime) + " ms.");}

在FilterFileManager的@PreDestroy方法shutdown中,主要是负责关闭poller Thread。

FilterFileManager启动关闭的流程如下图示:
在这里插入图片描述

根据类名加载filter的代码在FilterLoader.putFiltersForClasses()方法中:

    /*** Load and cache filters by className** @param classNames The class names to load* @return List of the loaded filters* @throws Exception If any specified filter fails to load, this will abort. This is a safety mechanism so we can* prevent running in a partially loaded state.*/public List<ZuulFilter> putFiltersForClasses(String[] classNames) throws Exception{List<ZuulFilter> newFilters = new ArrayList<>();for (String className : classNames){newFilters.add(putFilterForClassName(className));}return newFilters;}public ZuulFilter putFilterForClassName(String className) throws Exception{Class clazz = Class.forName(className);if (! ZuulFilter.class.isAssignableFrom(clazz)) {throw new IllegalArgumentException("Specified filter class does not implement ZuulFilter interface!");}else {ZuulFilter filter = filterFactory.newInstance(clazz);putFilter(className, filter, System.currentTimeMillis());return filter;}}

实现动态加载变化的filter功能是在filterLoader.putFilter(file)中实现的,其代码如下:

/*** 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.** @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
{try {String sName = file.getAbsolutePath();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 = filterFactory.newInstance(clazz);putFilter(sName, filter, file.lastModified());return true;}}}catch (Exception e) {LOG.error("Error loading filter! Continuing. file=" + String.valueOf(file), e);return false;}return false;
}

这段代码主要是通过比较传入的File的lastModified时间戳和map缓存中同名文件的时间戳来判定文件是否有变更,使用compiler去编译filter文件为Class对象,然后使用filterFactory去创建类实例。

至此,Zuul 2动态加载filter的机制已经介绍完毕,后续如果有时间会继续补充Zuul 2其他相关技术主题的分析文章。以两张UML图作为本篇文章的结语吧:
在这里插入图片描述

ZuulFilter相关的类后面的专题文章会具体展开介绍。

在这里插入图片描述

纵向的箭头表示F6,即step over;横向的箭头表示F5,即step in。需要注意的是,由于Guice容器托管的关系,有些箭头不是严格的表示调用的先后关系,对此大家意会就好了。


http://chatgpt.dhexx.cn/article/GabBin4Y.shtml

相关文章

Zuul2.1文档

Zuul2.1文档 What is Zuul?Why did we build Zuul?How We Use Zuul At NetflixGetting Started 2.0How It Works 2.0Architectural OverviewFilters Server ConfigurationServer ModesHTTPHTTP/2Mutual TLS FiltersIncomingEndpointOutgoingAsyncExtracting Body ContentUsef…

【微服务网关Zuul2】网关Zuul原理与实战

一、参考资料 Spring Cloud Netflix Zuul官方文档翻译 — Jbonehttps://jbone.cn/translate/spring-cloud-netflix-zuul/Spring Cloud Netflixhttps://docs.spring.io/spring-cloud-netflix/docs/2.2.9.RELEASE/reference/html/#router-and-filter-zuul

Zuul2 的 线程模型

Zuul2 的 线程模型 转自&#xff1a;https://www.jianshu.com/p/cb413fec1632 Zuul 2相对zuul 1 由同步改进为异步机制&#xff0c;没有了同步阻塞&#xff0c;全部基于事件驱动模型编程&#xff0c;线程模型也变得简单。 zuul做为一个网关接受客户端的请求–服务端&#xf…

Spring Cloud Gateway VS Netflix Zuul2

最近公司要引入统一网关&#xff0c;自己也参与调研了几种&#xff0c;当在研究Netflix的Zuul2和SpringCloudGateway时被网络上杂七杂八的材料跟震惊了&#xff0c;不客气地说很多国内博客都是在误人子弟&#xff0c;充斥着那些基于SpringCloud全家桶号称自己使用的是zuul2的“…

Zuul1和Zuul2该如何选择?

介绍 在今年5月中&#xff0c;Netflix终于开源了它的支持异步调用模式的Zuul网关2.0版本&#xff0c;真可谓千呼万唤始出来。从Netflix的官方博文[附录1]中&#xff0c;我们获得的信息也比较令人振奋&#xff1a; The Cloud Gateway team at Netflix runs and operates more t…

Zuul 2: Netflix的异步、无阻塞系统之旅

作者: Netflix Technology Blog 译者: java达人 来源: https://medium.com/netflix-techblog/zuul-2-the-netflix-journey-to-asynchronous-non-blocking-systems-45947377fb5c Zuul 2和它的“前辈”做了同样的事情—充当Netflix服务器基础设施的前门&#xff0c;处理来自全…

微服务架构:Zuul 1.0 和 2.0 我们该如何选择?

作者&#xff1a;架构师杨波 来源&#xff1a;波波微课 在今年5月中&#xff0c;Netflix终于开源了它的支持异步调用模式的Zuul网关2.0版本&#xff0c;真可谓千呼万唤始出来。从Netflix的官方博文[附录1]中&#xff0c;我们获得的信息也比较令人振奋&#xff1a; The Cloud Ga…

Zuul2核心架构

Zuul2的核心架构就是就是两大体系&#xff0c;netty体系和filter体系。 1 Netty体系 Zuul2底层采用Netty的事件响应模式&#xff0c;要掌握zuul2就必须先要掌握Netty。 1.1 Channel、Event、EventLoop、EventLoopGroup、ChannelHandler Channel&#xff1a;每一次通信就会启…

【Zuul2】网关Zuul控制台DashBoard

目录 一、需求背景 二、实现方案 一、源码获取 二、源码分析 三、效果展示 三、相关问题 一、需求背景 用JAVA为开发语言的流控网关主要分为以下三种&#xff1a; Netflix Zuul/Zuul2Spring Cloud GateWayAlibaba Sentinel 从定位上来看&#xff0c;Zuul2与SpringClou…

【Zuul2】Zuul2网关介绍及案例(非spring集成)

目录 一.使用缘由 二.项目介绍 1.核心内容 (1)三种过滤器 Inbound、Endpoint 、Outbound (2)配置文件application.properties (3)动态配置application.properties 2.参考文档 一.使用缘由 公司需要在springcloudgateway和zuul2间做一次较为完整的调研对比&#xff0c;…

time_t c语言 2038,什么是2038问题?

什么是2038问题 不知道你有没有听过2038问题?无论你是否听过,本文将带你认识什么是2038问题。 Unix时间戳 定义为从格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。 而在C语言中,常用time_t来表示。举个例子: #include #in…

MySQL的时间戳2038年问题还有16年,最好在设计上的时候使用datetime就可以了,不要使用时间戳字段了,即使用了也不要用int类型进行映射,使用long类型映射即可

目录 前言1&#xff0c;关于MySQL时间戳的2038年BUG2&#xff0c;使用Docker创建MySQL 模拟下3&#xff0c;总结 前言 本文的原文连接是: https://blog.csdn.net/freewebsys/article/details/127455169 未经博主允许不得转载。 博主CSDN地址是&#xff1a;https://blog.csdn.n…

计算机为什么不用三十二进制,32位进制导致2038年问题

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 在计算机应用上,2038年问题可能会导致某些软件在2038年无法正常工作。所有使用UNIX时 间表示时间的程序都将受其影响,因为它们以自1970年1月1日经过的秒数(忽略闰秒)来表示时间。 这种时间表示法在类Unix(Unix-like)操作系统上是…

2038年问题 linux内核5.6,Linux Kernel 5.6 开发者已率先做好准备 应对 2038 年问题

新十年伊始&#xff0c;Linux Kernel 5.6的开发者已经准备好着手解决将在下一个十年到来的2038年问题(又称“Y2038”或“Unix Y2K”问题)。Linux 5.6也成为第一个为32位系统准备运行到2038年之后的主线内核。 2038年问题与千年虫问题类似&#xff0c;它可能会导致某些软件在203…

mysql 2038年问题_时间戳(UnixTimestamp)与 《2038年问题》

时间戳是从格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。 现在时间戳的长度是十位(1435113975--2015/6/24 10:46:15)。 要到 2286/11/21 01:46:40 才会变成11位(10000000000)&#xff0c;距离现在还有 271年。 不同时区获取的…

2038年问题 linux内核5.6,Linux Kernel 5.6 开发者已准备好应对 2038 年问题

2038 年问题与千年虫问题类似&#xff0c;它可能会导致某些软件在 2038 年 1 月 19 日 3 时 14 分 07 秒之后无法正常工作。届时&#xff0c;在大部分 32 位操作系统上&#xff0c;依据 “time_t” 标准&#xff0c;时间将会“绕回”且在内部被表示为一个负数&#xff0c;并造成…

2038年问题 linux内核5.6,Linux 5.1延续为2038年问题所做的多年准备

Linux 5.1为2038年问题在内核方面继续进行大量的工作。 多年来在Linux内核一直看到“Y2038”的工作&#xff0c;而这一努力远未结束。Thomas Gleixner为Linux 5.1内核提交了最新的Y2038工作&#xff0c;在之前的内核中做了大量基础工作之后&#xff0c;Linux 5.1内核引入了一组…

2038计算机系统,2038年问题

2038年问题是指在使用POSIX时间的32位计算机应用程序上&#xff0c;格林尼治时间2038年1月19日凌晨03:14:07(北京时间&#xff1a;2038年1月19日中午11:14:07)之后无法正常工作。 中文名 2038年问题 外文名 Year 2038 problem概 念 计算机bug(程序错误) 载 体 使用POSIX时…

聊一聊2038年问题

庚子年是中国传统的 60 甲子纪年法。擅长观测的古人很早就发现&#xff0c;每当年份执行到庚子这一年&#xff0c;自然灾害变多&#xff0c;突发事件频频&#xff0c;一些震动世界、影响安定的大事件也容易发生在这一年。而我们现在所处的 2020 年就是新一轮的庚子年&#xff0…

List集合去重 --指定对象属性去重

在针对特定场景下,将获取到的list<T> 集合 按照某一个特定的对象中的属性进行去重操作,以下代码会将传入的集合进行指定去重,会将指定属性重复的对象 只保留第一个,后续的重复则不会保存到去重后的集合中,当然我们也可以通过集合的差异获取出重复的对象以及后续的再将去…