One, what is a dynamic filter
Zuul provides a framework that can dynamically load, compile, and run filters. These filters are written by Groovy and placed in a specific directory on Zuul Server. Zuul will poll these directories on a regular basis, and the modified filters will be dynamically loaded into Zuul Server. In this way, if you want to change the filter, you don’t need to republish the gateway, just upload the filter to the specified directory.
Below we will expand based on spring-cloud-starter-zuul (SpringCloud version is Edgware.SR3) to realize the function of dynamically loading Filter
Second, Zuul dynamic Filter implementation
1), add groovy dependency
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.12</version>
</dependency>
2), load the Groovy script
In normal development, it is sometimes necessary to implement functions that are executed after the project is started. A simple implementation solution provided by SpringBoot is to add a Bean and implement the CommandLineRunner interface. The code to implement the function is placed in the implemented run method.
For multiple implementation classes of the CommandLineRunner interface, specify the execution order through the @Order annotation
@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), write Groovy script
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
}
}
Now create a folder to store the filter in the idea directory
The directory where the gateway is loaded is specified in the startup parameters
-Dzuul.filter.root=/Users/hanxiantao/Desktop/学习笔记/Zuul深入学习/zuul_lab/lab05/zuul_gateway/groovy/filters
Do not put the Groovy script in the directory first, request the gateway, and there is no log printed in GroovyFilter
Then put the Groovy script in the directory, request the gateway, and print the log as follows:
Three, Zuul dynamically loads Filter source code analysis
Let’s take a look at how Zuul implements dynamic loading of Filter. In GroovyLoadLineRunner, we finally call the FilterFileManager init()
method.
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()
The method is finally called startPoller()
, and a daemon thread is opened here, which will always read files from the directory we specified in a loop, and then put them in the FilterLoader, thus realizing the function of dynamically loading the 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);
}
}