目錄
概述
zuul 支持過(guò)濾器動(dòng)態(tài)修改動(dòng)態(tài)加載功能把介,目前支持動(dòng)態(tài)Filter由Groovy編寫揽咕。動(dòng)態(tài)管理Groovy編寫的Filter類文件變更以及動(dòng)態(tài)編譯等功能。
實(shí)現(xiàn)原理
實(shí)現(xiàn)原理是監(jiān)控存放Filter文件的目錄嘶卧,定期掃描這些目錄,如果發(fā)現(xiàn)有新Filter源碼文件或者Filter源碼文件有改動(dòng)真屯,則對(duì)文件進(jìn)行編譯加載
Filter類文件動(dòng)態(tài)管理
通過(guò)原理知道zuul通過(guò)定期掃描Filter文件存放的目錄來(lái)校驗(yàn)是否有新的文件或有改動(dòng)的文件脸候,對(duì)這些文件進(jìn)行加載,源碼是通過(guò)FilterFileManager實(shí)現(xiàn)此功能的绑蔫。FilterFileManager管理目錄輪詢的變化和新的Groovy過(guò)濾器运沦。輪詢間隔和目錄在類的初始化中指定,并且輪詢器將檢查,更改和添加配深。
FilterFileManager 功能如下:
- 開(kāi)啟一個(gè)線程携添,開(kāi)始輪詢(startPoller)
- 每次輪詢,處理目錄內(nèi)的所有*.groovy文件篓叶,即調(diào)用FilterLoader.getInstance().putFilter(file)
- 輪詢部分
//開(kāi)啟輪詢線程
void startPoller() {
//創(chuàng)建輪詢線程
poller = new Thread("GroovyFilterFileManagerPoller") {
public void run() {
while (bRunning) {
try {
//定時(shí)休眠 sleep(pollingIntervalSeconds * 1000);
//掃描目錄監(jiān)聽(tīng)目錄變化
manageFiles();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
poller.setDaemon(true);
//開(kāi)啟輪詢線程
poller.start();
}
- startPoller 開(kāi)啟輪詢線程定時(shí)調(diào)用manageFiles方法執(zhí)行掃描目錄烈掠,監(jiān)聽(tīng)目錄變化
- startPoller方法在FilterFileManager初始化時(shí)(init)調(diào)用一次
- 目錄掃描以及檢測(cè)
//目錄掃描檢測(cè)主入口方法
void manageFiles() throws Exception, IllegalAccessException, InstantiationException {
// 1. 通過(guò)getFiles方法獲取到目錄的所有Filter文件
List<File> aFiles = getFiles();
//2. 通過(guò)processGroovyFiles方法處理所有Filter文件
processGroovyFiles(aFiles);
}
//加載所有動(dòng)態(tài)Filter文件
List<File> getFiles() {
List<File> list = new ArrayList<File>();
//遍歷配置的動(dòng)態(tài)Filter文件存放目錄羞秤,加載目錄文件
for (String sDirectory : aDirectories) {
if (sDirectory != null) {
File directory = getDirectory(sDirectory);
File[] aFiles = directory.listFiles(FILENAME_FILTER);
if (aFiles != null) {
list.addAll(Arrays.asList(aFiles));
}
}
}
return list;
}
//處理所有動(dòng)態(tài)Filter文件
void processGroovyFiles(List<File> aFiles) throws Exception, InstantiationException, IllegalAccessException {
//遍歷動(dòng)態(tài)Filter列表
for (File file : aFiles) {
//通過(guò)FilterLoader類來(lái)加載處理Filter
FilterLoader.getInstance().putFilter(file);
}
}
manageFiles方法完成兩件事
- 調(diào)用getFiles方法加載所有預(yù)制目錄中的動(dòng)態(tài)Filter文件
- 調(diào)用processGroovyFiles方法遍歷filter列表通過(guò)FilterLoader類處理加載Filter
Filter類文件動(dòng)態(tài)編譯
zuul 動(dòng)態(tài)加載filter文件,并通過(guò)編譯器將文件編譯成class
目前zuul通過(guò)定義DynamicCodeCompiler接口以及Groovy編譯的實(shí)現(xiàn)類GroovyCompiler來(lái)完成Groovy編寫的filter的動(dòng)態(tài)編譯
- DynamicCodeCompiler
public interface DynamicCodeCompiler {
/**
* 通過(guò)源碼字符串以及filter 名稱編譯class
*
*/
Class compile(String sCode, String sName) throws Exception;
/**
*
* 通過(guò)源碼文件編譯class
*/
Class compile(File file) throws Exception;
}
DynamicCodeCompiler 動(dòng)態(tài)代碼編譯器接口
定義兩個(gè)方法根據(jù)不同的參數(shù)返回編譯好的class
- GroovyCompiler
public class GroovyCompiler implements DynamicCodeCompiler {
private static final Logger LOG = LoggerFactory.getLogger(GroovyCompiler.class);
/**
* Compiles Groovy code and returns the Class of the compiles code.
*
* @param sCode
* @param sName
* @return
*/
@Override
public Class compile(String sCode, String sName) {
GroovyClassLoader loader = getGroovyClassLoader();
LOG.warn("Compiling filter: " + sName);
Class groovyClass = loader.parseClass(sCode, sName);
return groovyClass;
}
/**
* 獲取GroovyClassLoader
* @return a new GroovyClassLoader
*/
GroovyClassLoader getGroovyClassLoader() {
return new GroovyClassLoader();
}
/**
* 編譯groovy class從文件中
* Compiles groovy class from a file
*
* @param file
* @return
* @throws java.io.IOException
*/
@Override
public Class compile(File file) throws IOException {
GroovyClassLoader loader = getGroovyClassLoader();
Class groovyClass = loader.parseClass(file);
return groovyClass;
}
}
GroovyCompiler 為groovy編寫的Filter的動(dòng)態(tài)編譯實(shí)現(xiàn)類左敌。
zuul 默認(rèn)支持groovy編寫的Filter瘾蛋,這給在不停服的情況下動(dòng)態(tài)變更業(yè)務(wù)規(guī)則提供了可靠的保障