Zuul实现Groovy加载动态Filter
程序员文章站
2022-06-21 14:45:48
...
一、什么是动态Filter
Zuul提供了一个能够对过滤器进行动态的加载、编译、运行的框架。这些过滤器是由Groovy写成,被放在Zuul Server上的特定目录下面。Zuul会按期轮询这些目录,修改过的过滤器会动态的加载到Zuul Server中。这样如果要对过滤器有改动,就不用进行网关的重新发布了,只需要把过滤器上传到指定目录即可
下面我们就基于spring-cloud-starter-zuul(SpringCloud版本为Edgware.SR3)进行扩展,实现动态加载Filter的功能
二、Zuul动态Filter实现
1)、添加groovy依赖
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
2)、加载Groovy脚本
平常开发中有时需要实现在项目启动后执行的功能,SpringBoot提供的一种简单的实现方案就是添加一个Bean并实现CommandLineRunner接口,实现功能的代码放在实现的run方法中
多个CommandLineRunner接口的实现类时,通过@Order注解指定执行顺序
@Component
@Order(value = 1)
public class GroovyLoadLineRunner implements CommandLineRunner {
@Override
public void run(String... strings) throws Exception {
FilterLoader.getInstance().setCompiler(new GroovyCompiler());
//读取配置,获取脚本根目录
String scriptRoot = System.getProperty("zuul.filter.root", "groovy/filters");
//获取刷新间隔
String refreshInterval = System.getProperty("zuul.filter.refreshInterval", "5");
if (scriptRoot.length() > 0) {
scriptRoot = scriptRoot + File.separator;
}
FilterFileManager.setFilenameFilter(new GroovyFileFilter());
FilterFileManager.init(Integer.parseInt(refreshInterval), scriptRoot + "pre",
scriptRoot + "route", scriptRoot + "post");
}
}
3)、编写Groovy脚本
import com.netflix.zuul.ZuulFilter
import com.netflix.zuul.context.RequestContext
import org.apache.catalina.servlet4preview.http.HttpServletRequest
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants
class GroovyFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(GroovyFilter.class)
@Override
String filterType() {
return FilterConstants.PRE_TYPE
}
//过滤器优先级
@Override
int filterOrder() {
return 5
}
@Override
boolean shouldFilter() {
return true
}
@Override
Object run() {
HttpServletRequest request = (HttpServletRequest) RequestContext.getCurrentContext().getRequest()
Enumeration<String> headerNames = request.getHeaderNames()
while (headerNames.hasMoreElements()) {
String name = (String) headerNames.nextElement()
String value = request.getHeader(name)
LOGGER.info("header: " + name + ":" + value)
}
LOGGER.info("This is Groovy Filter")
return null
}
}
现在idea目录中创建存放过滤器的文件夹
启动参数中指定加载网关的目录
-Dzuul.filter.root=/Users/hanxiantao/Desktop/学习笔记/Zuul深入学习/zuul_lab/lab05/zuul_gateway/groovy/filters
先不把Groovy脚本放到目录下,请求网关,并没有GroovyFilter中打印的日志
再把Groovy脚本放到目录下,请求网关,打印日志如下:
三、Zuul动态加载Filter源码解析
下面我们来看下Zuul是如何实现动态加载Filter的,GroovyLoadLineRunner中我们最后调用了FilterFileManager的init()
方法
public class FilterFileManager {
/**
* Initialized the GroovyFileManager.
*
* @param pollingIntervalSeconds the polling interval in Seconds
* @param directories Any number of paths to directories to be polled may be specified
* @throws IOException
* @throws IllegalAccessException
* @throws InstantiationException
*/
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();
}
init()
方法最后调用了startPoller()
,这里开启了一个守护线程,会一直循环从我们指定的目录下读取文件,然后放到FilterLoader中,从而实现了动态加载Filter的功能
public class 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();
}
void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
List<File> aFiles = getFiles();
processGroovyFiles(aFiles);
}
/**
* puts files into the FilterLoader. The FilterLoader will only addd new or changed filters
*
* @param aFiles a List<File>
* @throws IOException
* @throws InstantiationException
* @throws IllegalAccessException
*/
void processGroovyFiles(List<File> aFiles) throws Exception, InstantiationException, IllegalAccessException {
for (File file : aFiles) {
FilterLoader.getInstance().putFilter(file);
}
}