zuul2 FilterFileManager 源码学习
2020-07-12 本文已影响0人
pcgreat
感想:
1 zuul2 没有使用spring 依赖注入 ,bean 管理 ,甚至任何一个spring依赖你都找不到 看来和 spring关系真的很差 ,要不然也不用spring 自己写个网关,不用奈飞天的,而使用google guice 高效简单 ,当然他也不需要spring 全家桶。
2 此类会起一线程管理groovy filter目录轮询以查找all Groovy过滤器 file,并异步加载他们,加载逻辑在DynamicFilterLoader。轮询间隔和目录是在类的初始化中指定的
3 这个类在项目其他地方没有看到任何的引用 ,也就是说,生成Singleton instance 它就像一个孤岛一样的存在
5 还有些小细节 static nest class 的使用 , thread daemon 使用场景 都体现了人家开发功力。
/*
* Copyright 2018 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.zuul;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.netflix.config.DynamicIntProperty;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class manages the directory polling for changes and new Groovy filters.
* Polling interval and directories are specified in the initialization of the class, and a poller will check
* for changes and additions.
*
* @author Mikey Cohen
* Date: 12/7/11
* Time: 12:09 PM
*/
@Singleton
public class FilterFileManager {
private static final Logger LOG = LoggerFactory.getLogger(FilterFileManager.class);
// 异步加载 groovy filter 线程数
private static final DynamicIntProperty FILE_PROCESSOR_THREADS = new DynamicIntProperty("zuul.filterloader.threads", 1);
// 异步加载 all 变更groovy filter 任务超时时间 ,默认 120s
private static final DynamicIntProperty FILE_PROCESSOR_TASKS_TIMEOUT_SECS = new DynamicIntProperty("zuul.filterloader.tasks.timeout", 120);
// 轮询检查groovy filter ,将变更filters 添加异步加载 groovy filter 线程池
Thread poller;
boolean bRunning = true;
private final FilterFileManagerConfig config;
private final FilterLoader filterLoader;
// 变更filters 添加异步加载 groovy filter 线程池
private final ExecutorService processFilesService;
@Inject
public FilterFileManager(FilterFileManagerConfig config, FilterLoader filterLoader) {
this.config = config;
this.filterLoader = filterLoader;
ThreadFactory tf =
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("FilterFileManager_ProcessFiles-%d").build();
this.processFilesService = Executors.newFixedThreadPool(FILE_PROCESSOR_THREADS.get(), tf);
}
/**
* Initialized the GroovyFileManager.
*
* @throws Exception
*/
@Inject
public 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.");
}
/**
* Shuts down the poller
*/
public void shutdown() {
stopPoller();
}
void stopPoller() {
bRunning = false;
}
// 管理groovy filter目录轮询以查找更改和新的Groovy过滤器,并将filters 交给异步加载线程池
void startPoller() {
poller = new Thread("GroovyFilterFileManagerPoller") {
{
setDaemon(true);
}
public void run() {
while (bRunning) {
try {
sleep(config.getPollingIntervalSeconds() * 1000);
manageFiles();
}
catch (Exception e) {
LOG.error("Error checking and/or loading filter files from Poller thread.", e);
}
}
}
};
poller.start();
}
/**
* Returns the directory File for a path. A Runtime Exception is thrown if the directory is in valid
*
* @param sPath
* @return a File representing the directory path
*/
public File getDirectory(String sPath) {
File directory = new File(sPath);
if (!directory.isDirectory()) {
URL resource = FilterFileManager.class.getClassLoader().getResource(sPath);
try {
directory = new File(resource.toURI());
} catch (Exception e) {
LOG.error("Error accessing directory in classloader. path=" + sPath, e);
}
if (!directory.isDirectory()) {
throw new RuntimeException(directory.getAbsolutePath() + " is not a valid directory");
}
}
return directory;
}
/**
* Returns a List<File> of all Files from all polled directories
*
* @return
*/
List<File> getFiles() {
List<File> list = new ArrayList<File>();
for (String sDirectory : config.getDirectories()) {
if (sDirectory != null) {
File directory = getDirectory(sDirectory);
File[] aFiles = directory.listFiles(config.getFilenameFilter());
if (aFiles != null) {
list.addAll(Arrays.asList(aFiles));
}
}
}
return list;
}
/**
* puts files into the FilterLoader. The FilterLoader will only add new or changed filters
*
* @param aFiles a List<File>
* @throws IOException
* @throws InstantiationException
* @throws IllegalAccessException
*/
void processGroovyFiles(List<File> aFiles) throws Exception {
List<Callable<Boolean>> tasks = new ArrayList<>();
for (File file : aFiles) {
tasks.add(() -> {
try {
return filterLoader.putFilter(file);
}
catch(Exception e) {
LOG.error("Error loading groovy filter from disk! file = " + String.valueOf(file), e);
return false;
}
});
}
processFilesService.invokeAll(tasks, FILE_PROCESSOR_TASKS_TIMEOUT_SECS.get(), TimeUnit.SECONDS);
}
void manageFiles()
{
try {
List<File> aFiles = getFiles();
processGroovyFiles(aFiles);
}
catch (Exception e) {
String msg = "Error updating groovy filters from disk!";
LOG.error(msg, e);
throw new RuntimeException(msg, e);
}
}
public static class FilterFileManagerConfig
{
private String[] directories;
private String[] classNames;
private int pollingIntervalSeconds;
private FilenameFilter filenameFilter;
public FilterFileManagerConfig(String[] directories, String[] classNames, int pollingIntervalSeconds, FilenameFilter filenameFilter) {
this.directories = directories;
this.classNames = classNames;
this.pollingIntervalSeconds = pollingIntervalSeconds;
this.filenameFilter = filenameFilter;
}
public String[] getDirectories() {
return directories;
}
public String[] getClassNames()
{
return classNames;
}
public int getPollingIntervalSeconds() {
return pollingIntervalSeconds;
}
public FilenameFilter getFilenameFilter() {
return filenameFilter;
}
}
}