Zuul实现Groovy加载动态Filter

一、什么是动态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);
        }
    }  

猜你喜欢

转载自blog.csdn.net/qq_40378034/article/details/109558790
今日推荐