精华内容
下载资源
问答
  • Zuul Filter

    万次阅读 2018-10-30 17:51:43
    文章目录简介Filter 类型Zuul 原生 Filter 简介   Zuul 的核心逻辑是由一系列紧密配合工作的 Filter 来实现的,它们能够在进行 HTTP 请求或者响应的时候执行相关操作。可以说,没有 Filter 责任链,就没有如今的 ...

    简介

      Zuul 的核心逻辑是由一系列紧密配合工作的 Filter 来实现的,它们能够在进行 HTTP 请求或者响应的时候执行相关操作。可以说,没有 Filter 责任链,就没有如今的 Zuul,更不可能构成功能丰富的网关。基本上你想要在网关实现的功能都要与 Filter 有关。它是 Zuul 中最为开放与核心的功能。 Zuul Filter 的主要特性有以下几点:

    • Filter 的类型:Filter 的类型决定了此 Filter 在 Filter 链中的执行顺序。可能是路由动作发生前,可能是路由动作发生时,可能是路由动作发生后,也可能是路由过程发生异常时。
    • Filter 的执行顺序:同一种类型的 Filter 可以通过 flterOrder() 方法来设定执行顺序。一般会根据业务的执行顺序需求,来设定自定义 Filter 的执行顺序。
    • Filter 的执行条件:Filter 运行所需要的标准或条件。
    • Filter 的执行效果:符合某个 Filter 执行条件,产生的执行效果。

      Zuul 内部提供了一个动态读取、编译和运行这些 Filter 的机制。Filter 之间不直接通信,在请求线程中会通过 RequestContext 来共享状态,它的内部是用 ThreadLocal 实现的,当然你也可以在 Filter之间使用 ThreadLocal 来收集自己需要的状态或数据。

    Filter 类型

      Zuul 中不同类型 filter 的执行逻辑核心在 com.netflix.zuul.http.ZuulServlet 类中定义,该类相关代码如下:

    @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
    
            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();
    
            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }
    
        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }
    

    流程图

      这张经典的官方流程图有些问题,其中 post Filter 抛错之后进入 error Filter,然后再进入 post Filter 是有失偏颇的。实际上 post Filter 抛错分两种情况:

    1. 在 post Filter 抛错之前,pre、route Filter 没有抛错,此时会进入 ZuulException 的逻辑,打印堆栈信息,然后再返回 status = 500 的 ERROR 信息。
    2. 在 post Filter 抛错之前,pre、route Filter 已有抛错,此时不会打印堆栈信息,直接返回status = 500 的 ERROR 信息。

      也就是说,整个责任链流程终点不只是 post Filter,还可能是 error Filter,这里重新整理了一下,如图:

    流程图

      这样就比较直观地描述了 Zuul 关于 Filter 的请求生命周期。Zuul 中一共有四种不同生命周期的 Filter,分别是:

    • pre:在 Zuul 按照规则路由到下级服务之前执行。如果需要对请求进行预处理,比如鉴权、限流等,都应考虑在此类 Filter 实现。
    • route:这类 Filter 是 Zuul 路由动作的执行者,是 Apache Http Client 或 Netflix Ribbon 构建和发送原始 HTTP 请求的地方,目前已支持 Okhttp。
    • post:这类 Filter 是在源服务返回结果或者异常信息发生后执行的,如果需要对返回信息做一些处理,则在此类 Filter 进行处理。
    • error:在整个生命周期内如果发生异常,则会进入 error Filter,可做全局异常处理。

      在实际项目中,往往需要自实现以上类型的 Filter 来对请求链路进行处理,根据业务的需求,选取相应生命周期的 Filter 来达成目的。在 Filter 之间,通过 com.netflix.zuul.context.RequestContext 类来进行通信,内部采用 ThreadLocal 保存每个请求的一些信息,包括请求路由、错误信息、HttpServletRequest、HttpServletResponse,这使得一些操作是十分可靠的,它还扩展了 ConcurrentHashMap,目的是为了在处理过程中保存任何形式的信息。

    Zuul 原生 Filter

      官方文档提到, Zuul Server 如果使用 @EnableZuulProxy 注解搭配 Spring Boot Actuator,会多两个管控端点(注意要开启对应的端点)。

    1. /routes

    routes

    routes details

    1. /filters

    filters

      在 /filters 接口中会返回很多的 Filter 信息,包括:类路径、执行顺序、是否被禁用、是否静态。可以组合成如下图:

    Filters 链

      说明如下:

    Filter 功能

      以上是使用 @EnableZuulProxy 注解后安装的 Filter,如果使用 @EnableZuulServer 将缺少 PreDecorationFilterRibbonRoutingfilterSimpleHostRoutingFilter

      如果你不想使用原生的这些功能,可以采取替代实现的方式,覆盖掉其原生代码,也可以采取禁用策略,语法如下:

    zuul.<SimpleClassName>.<flterType>.disable=true
    

      比如要禁用 SendErrorFilter,在配置文件中添加 zuul.SendErrorFilter.error.disable=true即可。

    自定义 Filter

    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.exception.ZuulException;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;
    
    /**
     * @Author:大漠知秋
     * @Description:测试使用第一个 pre Filter
     * @CreateDate:4:34 PM 2018/10/30
     */
    @Component
    @Slf4j
    public class FirstPreFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
    
            log.info("经过第一个 pre 过滤器");
    
            return null;
    
        }
    
    }
    

    日志

    Filter 小例

    • 常量类
    /**
     * @Author:大漠知秋
     * @Description:会话相关常量
     * @CreateDate:5:10 PM 2018/10/30
     */
    public interface SessionContants {
    
        String LOGIC_IS_SUCCESS = "LOGIC_IS_SUCCESS";
    
        String ERROR_RESPONSE_BODY = "{\"status\": 10600, \"msg\":\"%s\"}";
    
    }
    
    • PreFirstFilter
    /**
     * @Author:大漠知秋
     * @Description:测试使用第一个 pre Filter
     * @CreateDate:4:34 PM 2018/10/30
     */
    @Component
    @Slf4j
    public class PreFirstFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
    
            log.info("经过第一个 pre 过滤器");
    
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            if (StringUtils.isBlank(request.getHeader("a"))) {
                // 未经过逻辑
                // 用来给后面的 Filter 标识,是否继续执行
                ctx.set(SessionContants.LOGIC_IS_SUCCESS, false);
                // 返回信息
                ctx.setResponseBody(String.format(SessionContants.ERROR_RESPONSE_BODY, "a Header头不足"));
                // 对该请求禁止路由,禁止访问下游服务
                ctx.setSendZuulResponse(false);
                return null;
            }
    
            // 用来给后面的 Filter 标识,是否继续执行
            ctx.set(SessionContants.LOGIC_IS_SUCCESS, true);
            return null;
    
        }
    
    }
    
    • PreSecondFilter
    /**
     * @Author:大漠知秋
     * @Description:测试使用第二个 pre Filter
     * @CreateDate:4:34 PM 2018/10/30
     */
    @Component
    @Slf4j
    public class PreSecondFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return PRE_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return 2;
        }
    
        @Override
        public boolean shouldFilter() {
            RequestContext ctx = RequestContext.getCurrentContext();
            return (boolean) ctx.get(SessionContants.LOGIC_IS_SUCCESS);
        }
    
        @Override
        public Object run() throws ZuulException {
    
            log.info("经过第二个 pre 过滤器");
    
            RequestContext ctx = RequestContext.getCurrentContext();
            HttpServletRequest request = ctx.getRequest();
            if (StringUtils.isBlank(request.getParameter("b"))) {
                // 未经过逻辑
                // 用来给后面的 Filter 标识,是否继续执行
                ctx.set(SessionContants.LOGIC_IS_SUCCESS, false);
                // 返回信息
                ctx.setResponseBody(String.format(SessionContants.ERROR_RESPONSE_BODY, "b 参数头不足"));
                // 对该请求禁止路由,禁止访问下游服务
                ctx.setSendZuulResponse(false);
                return null;
            }
    
            // 用来给后面的 Filter 标识,是否继续执行
            ctx.set(SessionContants.LOGIC_IS_SUCCESS, true);
            return null;
    
        }
    
    }
    
    • PostFirstFilter
    /**
     * @Author:大漠知秋
     * @Description:测试使用第一个 post Filter
     * @CreateDate:4:34 PM 2018/10/30
     */
    @Component
    @Slf4j
    public class PostFirstFilter extends ZuulFilter {
    
        @Override
        public String filterType() {
            return POST_TYPE;
        }
    
        @Override
        public int filterOrder() {
            return 0;
        }
    
        @Override
        public boolean shouldFilter() {
            return true;
        }
    
        @Override
        public Object run() throws ZuulException {
    
            log.info("经过第一个 post 过滤器");
    
            RequestContext ctx = RequestContext.getCurrentContext();
            String responseBody = ctx.getResponseBody();
            if (StringUtils.isNotBlank(responseBody)) {
                // 说明逻辑没有通过
                // 设置编码
                ctx.getResponse().setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                ctx.getResponse().setCharacterEncoding("UTF-8");
                // 更改响应状态
                ctx.setResponseStatusCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
                // 替换掉响应报文
                ctx.setResponseBody(responseBody);
            }
    
            return null;
    
        }
    
    }
    

    结构图

      测试:

    测试

    源码:https://github.com/SlowSlicing/demo-spring-cloud-finchley/tree/ZuulFilter

    展开全文
  • 主要介绍了spring cloud-zuulFilter使用详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 一、什么是动态Filter Zuul提供了一个能够对过滤器进行动态的加载、编译、运行的框架。...下面我们就基于spring-cloud-starter-zuul(SpringCloud版本为Edgware.SR3)进行扩展,实现动态加载Filter的功能 二、Zuul

    一、什么是动态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);
            }
        }  
    
    展开全文
  • 通过上一节(zuul的各种配置)的学习,我们学会了zuul路由的各种配置,这一节我们来实现一下zuul的过滤器功能。那么为什么需要用到zuul的过滤器呢?我们知道zuul是我们实现外部系统统一访问的入口,那么我们就可以在...

        通过上一节(zuul的各种配置)的学习,我们学会了zuul路由的各种配置,这一节我们来实现一下zuul的过滤器功能。那么为什么需要用到zuul的过滤器呢?我们知道zuul是我们实现外部系统统一访问的入口,那么我们就可以在 zuul 上实现 api的鉴权操作,实现微服务的统一鉴权、给微服务的响应增加额外的响应头等。

    实现功能

        1、在网关增加一个 pre 类型的过滤器完成一个简单的权限的校验

        2、在网关增加一个 post 类型的过滤器完成增加一个相应头

        3、禁用某个过滤器

    代码结构

    eureka-server
        |- 服务注册中心
        zuul
            product-provider-8202
            product-provider-8203
                |- 服务提供者
            product-consumer-8201
                |- 服务消费者
            product-gateway-filters-8205
                |- 网关程序,演示 过滤器 的使用

        代码和上一节的代码差不多,只是新建了一个网关程序,在网关中增加了过滤器。

    zuul过滤器的四种类型
        pre:
            这种类型的过滤器在请求被路由到具体的服务之前进行调用,这个时候我们就可以进行权限的验证等等
        route:
            这种类型的过滤器将请求路由到具体的微服务。
        post:
            路由到微服务之后的处理,比如操作响应(Response),增加相应头、输出额外的内容
        error:
            当pre、route、post阶段发生异常时进行异常的处理

    过滤器中各个方法的含义

            @Override
    	public String filterType() {
    	}
    	@Override
    	public int filterOrder() {
    	}
    	@Override
    	public boolean shouldFilter() {
    	}
    	@Override
    	public Object run() {
    		return null;
    	}

        filterType:

            |- 表示过滤器的类型,即在那个阶段拦截,可以使用 FilterConstants 中的常量

        filterOrder:

            |- 过滤器的顺序,在同一组类型的过滤器中返回的值越小,越早执行。

               >> pre   类型建议在 FilterConstants.PRE_DECORATION_FILTER_ORDER  之前执行

               >> post  类型建议在 FilterConstants.SEND_RESPONSE_FILTER_ORDER  之前执行

        shouldFilter:

             |- true: 表示该过滤器执行 false:表示不执行该过滤器

        run:

             |- 执行具体的过滤器逻辑,返回值一般返回 null 即可。

        在过滤器中,如果当前请求不合法,可以跑出异常或设置 RequestContext.getCurrentContext().setSendZuulResponse(false) 来阻止 route 类型的过滤器执行。

    zuul过滤器的生命周期

    一、官网zuul的请求生命周期图

     

     

        解释:从上图可以看出,一次请求先经过 pre或 custom过滤器进行请求的预处理,然后通过 route 类型的过滤器将请求路由到据图的微服务,最终经过 post 类型的过滤器对响应数据进行处理。这三个阶段(pre、route、post)过程中发生了异常都会经过 error 类型的过滤器进行处理,error 处理完之后再次经过 post 类型的过滤器进行处理。

    二、从代码(ZuulServlet)中看各个过滤器执行的顺序

     

    代码编写

    一、注册中心、服务提供者、服务消费者(略)

    二、网关程序编写

           ①、引入 zuul 的依赖

    <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>

     

          ② 启动类上增加 @EnableZuulProxy 注解

    @SpringBootApplication
    @EnableZuulProxy
    @EnableDiscoveryClient
    public class ProductGatewayZuulApplication8205 {
    
    	public static void main(String[] args) {
    		SpringApplication.run(ProductGatewayZuulApplication8205.class, args);
    	}
    
    }

     

           ③、编写 pre 过滤器,进行权限判断

                  只要能从 request 中 获取到 token 的参数的值,那么认为通过,否则不通过。

    package com.huan.study.zuul.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import com.netflix.zuul.exception.ZuulException;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    
    /**
     * Token 认证前置过滤器
     *
     * @author huan.fu
     * @date 2018/6/12 - 16:34
     */
    @Component
    public class TokenAuthenticationFilter extends ZuulFilter {
    
    	private static final String TOKEN_PARAMETER = "token";
    
    	@Override
    	public String filterType() {
    		// pre 类型的过滤器
    		return FilterConstants.PRE_TYPE;
    	}
    
    	@Override
    	public int filterOrder() {
    		// token 检验应该放在第一位来进行校验,因此需要放在最前面
    		return FilterConstants.SERVLET_DETECTION_FILTER_ORDER - 1;
    	}
    
    	@Override
    	public boolean shouldFilter() {
    		// 过滤器是否应该执行, true:表示应该执行  false:表示跳过这个过滤器执行
    		return true;
    	}
    
    	@Override
    	public Object run() {
    		RequestContext requestContext = RequestContext.getCurrentContext();
    		// 获取到 request
    		HttpServletRequest request = requestContext.getRequest();
    		// 判断请求参数中是否存在  token  参数
    		String token = request.getParameter(TOKEN_PARAMETER);
    		if (StringUtils.isBlank(token)) {
    			// 不进行路由 ===> 即 route 类型的过滤器不执行
    			requestContext.setSendZuulResponse(false);
    			throw new ZuulRuntimeException(new ZuulException(this.filterType() + ":" + this.getClass().getSimpleName(), HttpStatus.UNAUTHORIZED.value(), "token参数不可为空"));
    		}
    		return null;
    	}
    }
    

        注意:

                1、RequestContext 为一个 ConcurrentHashMap, 并且里面的值是从 ThreadLocal 中获取的

                2、可以从RequestContext中获取到 request 和 response 等等

                3、requestContext.setSendZuulResponse(false)导致 route 类型的过滤器不进行执行

                4、如果不想自己写 error 类型的过滤器,那么默认由 SendErrorFilter 进行处理异常,如果我们想返回一个 401 的状态码给前台,出错的地方可以跑出一个 ZuulRuntimeException.

                5、自己写的 过滤器 需要被 Spring 管理即可

          

           ④、编写一个 post 类型的过滤器,增加一个额外的响应头

                 添加一个 new-header 的响应头

    package com.huan.study.zuul.filter;
    
    import com.netflix.zuul.ZuulFilter;
    import com.netflix.zuul.context.RequestContext;
    import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * 添加一个新的相应头
     *
     * @author huan.fu
     * @date 2018/6/12 - 18:01
     */
    @Component
    public class AddNewHeaderFilter extends ZuulFilter {
    
    	@Override
    	public String filterType() {
    		return FilterConstants.POST_TYPE;
    	}
    
    	@Override
    	public int filterOrder() {
    		return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
    	}
    
    	@Override
    	public boolean shouldFilter() {
    		return true;
    	}
    
    	@Override
    	public Object run() {
    		RequestContext requestContext = RequestContext.getCurrentContext();
    		HttpServletResponse response = requestContext.getResponse();
    		response.addHeader("new-header", "new-header");
    
    		return null;
    	}
    }
    

          ⑤、运行结果

     

     

    禁用过滤器

        有些时候我们可能想禁用某一类型的过滤器,比如异常返回的过滤器,每个公司可能都有自己异常返回的一套风格,如果我们不想使用 Zuul 默认的异常过滤器,那么就可以禁用掉这个过滤器。

     语法格式:

    zuul.<SimpleClassName>.<filterType>.disable=true

     禁用 SendErrorFilter

    zuul:
      SendErrorFilter:
        error:
          disable: true

     访问网关服务的 /filters 端点,看是否禁用掉

     

    完整代码

        网关层的代码: https://gitee.com/huan1993/spring-cloud-parent/tree/master/zuul

        网关过滤器的代码: https://gitee.com/huan1993/spring-cloud-parent/tree/master/zuul/product-gateway-filters-8205

    展开全文
  • Zuul 2是如何动态加载Filter的?

    千次阅读 2019-05-31 18:38:52
    Zuul 2沿用了Zuul 1的责任链模式的设计,其网关核心功能还是通过Filter链来实现的。要熟练使用和扩展Zuul 2的功能,必须要了解其Filter的加载和执行机制。另外,Zuul 2使用Guice作为依赖注入工具,因此在开始分析...

    Zuul 2沿用了Zuul 1的责任链模式的设计,其网关核心功能还是通过Filter链来实现的。要熟练使用和扩展Zuul 2的功能,必须要了解其Filter的加载和执行机制。另外,Zuul 2使用Guice作为依赖注入工具,因此在开始分析之前,我们需要大致了解Guice的基本原理和用法,传送门:Guide to Google Guice

    为了了解Zuul 2的filter加载机制,我们从入口开始看起。在官方提供的zuul-sample项目中,启动类是Bootstrap,在其start方法中,可以看到这么几行:

    ConfigurationManager.loadCascadedPropertiesFromResources("application");
    Injector injector = InjectorBuilder.fromModule(new ZuulSampleModule()).createInjector();
    BaseServerStartup serverStartup = injector.getInstance(BaseServerStartup.class);
    server = serverStartup.server();
    

    在上述代码中使用了ConfigurationManager去加载application.properties配置文件,然后通过Guice创建ZuulSampleModule,接着创建BaseServerStartup和Server实例。
    在ZuulSampleModule的configure方法中,跟filter加载相关的是如下两行:

    install(new ZuulFiltersModule());
    bind(FilterFileManager.class).asEagerSingleton();
    

    在ZuulFiltersModule的configure方法中,主要做的事情是指定了GroovyCompiler、GuiceFilterFactory和BasicFilterUsageNotifier的Guice注入绑定。而provideFilterFileManagerConfig方法,则是根据配置文件中zuul.filters.locations属性按照路径加载filterLocations,以及根据zuul.filters.packageszuul.filters.classes属性按照类名加载filterClassNames,然后据此创建FilterFileManagerConfig对象并返回。

    FilterFileManagerConfig对象的属性如下:

    private String[] directories; // filter文件夹路径
    private String[] classNames; // filter类名
    private int pollingIntervalSeconds; // 扫描时间间隔,秒数
    private FilenameFilter filenameFilter; // 文件名过滤器
    

    FilterFileManager会根据FilterFileManagerConfig指定的配置,使用FilterLoader执行filter文件加载,同样地这两个实例也是通过Guice注入到FilterFileManager的构造器中。
    FilterLoader的类图如下:
    在这里插入图片描述

    FilterLoader其主要功能是按照类名、文件名、脚本内容等方式使用DynamicCodeCompiler去加载为ZuulFilter实例,为了只加载新增或者变更了的filter文件,其内部使用了一些ConcurrentMap记住已经加载过的filter及其上次修改时间戳。FilterRegistry是filter注册表,其类图如下:
    在这里插入图片描述

    在FilterFileManager的构造器中,除了设置相关属性以外,还启动了名为processFilesService的固定大小线程池,主要是为了能够异步地使用filterLoader去动态加载filter文件。

    在FilterFileManager的@PostConstruct方法init中,主要做了三件事情:

    • 加载config中classNames属性直接指定的filter;
    • 加载config中directories属性指定的路径下的filter;
    • 启动一个名为poller的Thread去定时检测config中directories属性指定的路径下的filter文件是否有更新;
      其代码如下:
        /**
         * Initialized the GroovyFileManager.
         *
         * @throws Exception
         */
        @PostConstruct
        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.");
        }
    

    在FilterFileManager的@PreDestroy方法shutdown中,主要是负责关闭poller Thread。

    FilterFileManager启动关闭的流程如下图示:
    在这里插入图片描述

    根据类名加载filter的代码在FilterLoader.putFiltersForClasses()方法中:

        /**
         * Load and cache filters by className
         *
         * @param classNames The class names to load
         * @return List of the loaded filters
         * @throws Exception If any specified filter fails to load, this will abort. This is a safety mechanism so we can
         * prevent running in a partially loaded state.
         */
        public List<ZuulFilter> putFiltersForClasses(String[] classNames) throws Exception
        {
            List<ZuulFilter> newFilters = new ArrayList<>();
            for (String className : classNames)
            {
                newFilters.add(putFilterForClassName(className));
            }
            return newFilters;
        }
    
        public ZuulFilter putFilterForClassName(String className) throws Exception
        {
            Class clazz = Class.forName(className);
            if (! ZuulFilter.class.isAssignableFrom(clazz)) {
                throw new IllegalArgumentException("Specified filter class does not implement ZuulFilter interface!");
            }
            else {
                ZuulFilter filter = filterFactory.newInstance(clazz);
                putFilter(className, filter, System.currentTimeMillis());
                return filter;
            }
        }
    

    实现动态加载变化的filter功能是在filterLoader.putFilter(file)中实现的,其代码如下:

    /**
     * From a file this will read the ZuulFilter source code, compile it, and add it to the list of current filters
     * a true response means that it was successful.
     *
     * @param file
     * @return true if the filter in file successfully read, compiled, verified and added to Zuul
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IOException
     */
    public boolean putFilter(File file) throws Exception
    {
        try {
            String sName = file.getAbsolutePath();
            if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) {
                LOG.debug("reloading filter " + sName);
                filterRegistry.remove(sName);
            }
            ZuulFilter filter = filterRegistry.get(sName);
            if (filter == null) {
                Class clazz = compiler.compile(file);
                if (!Modifier.isAbstract(clazz.getModifiers())) {
                    filter = filterFactory.newInstance(clazz);
                    putFilter(sName, filter, file.lastModified());
                    return true;
                }
            }
        }
        catch (Exception e) {
            LOG.error("Error loading filter! Continuing. file=" + String.valueOf(file), e);
            return false;
        }
    
        return false;
    }
    

    这段代码主要是通过比较传入的File的lastModified时间戳和map缓存中同名文件的时间戳来判定文件是否有变更,使用compiler去编译filter文件为Class对象,然后使用filterFactory去创建类实例。

    至此,Zuul 2动态加载filter的机制已经介绍完毕,后续如果有时间会继续补充Zuul 2其他相关技术主题的分析文章。以两张UML图作为本篇文章的结语吧:
    在这里插入图片描述

    ZuulFilter相关的类后面的专题文章会具体展开介绍。

    在这里插入图片描述

    纵向的箭头表示F6,即step over;横向的箭头表示F5,即step in。需要注意的是,由于Guice容器托管的关系,有些箭头不是严格的表示调用的先后关系,对此大家意会就好了。

    展开全文
  • 主要介绍了SpringCloud Zuul自定义filter代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • ZuulFilter详解

    千次阅读 2018-12-10 17:11:59
    每个Filter的源代码都写入Zuul服务器上的一组指定目录,这些目录会定期轮询更改。更新的过滤器从磁盘读取,动态编译到正在运行的服务器中,并由Zuul为每个后续请求调用。 过滤类型 Zuul大部分功能都是通过过滤器...
  • Spring Cloud Zuul实现多级自定义Filter

    万次阅读 2018-11-02 11:30:43
    这篇博客不会去介绍关于zuul的基础知识以及配置详解,我会以代码的形式一步一步带领大家实现,利用多种或一种不同类型的过滤器进行处理相应的业务,以便让大家了解各个过滤器什么时候用,用来干什么,解决大家实际...
  • 主要给大家介绍了关于Spring Cloud Zuul过滤器的相关资料,通过阅读本文您将了解:Zuul过滤器类型与请求生命周期、如何编写Zuul过滤器、如何禁用Zuul过滤器和Spring Cloud为Zuul编写的过滤器及其功能,需要的朋友可以...
  • Zuul-自定义Filter

    千次阅读 2019-03-26 02:14:40
    自定义过滤器 ...其中filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()的两个方法。 filterType():定义过滤器的类型,它有4种类型,分别是pre、post、routing和error filt...
  • Zuul动态路由及动态Filter实现

    万次阅读 2017-07-19 18:15:55
    一, Zuul动态路由实现 动态路由需要达到可持久化配置,动态刷新的效果。不仅要能满足从spring的配置文件properties加载路由信息,还需要从Redis加载我们的配置。另外一点是,路由信息在容器启动时就已经加载进入...
  • zuul网关Filter处理流程及异常处理

    万次阅读 2017-09-08 14:25:30
    上一篇介绍了java网关Zuul的简单使用,进行请求路由转发和过滤器的基本操作。这一篇主要看一下它的过滤器Filter的工作流程及异常处理。首先看到Filter的四个方法,FilterType,filterOrder,shouldFilter,run。...
  • zuul 1:pre,route,post zuul 2:初步非双边 Zuul是一项边缘服务,可提供动态路由,监视,弹性,安全性等。 请查看维基的用法,信息,HOWTO等 这里有一些链接可以帮助您了解有关Zuul项目的更多信息。 随时向PR...
  • zuul filter拦截请求参数并修改返回

    千次阅读 2019-08-28 14:02:27
    import ... import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.http.ServletInputStreamWrapper; import net.sf.json...
  • 由于之前项目请求路径的命名格式是驼峰式的,而zuul中service-id是用“-”进行分隔的命名方式,使用默认配置是无法将原有url地址通过zuul转发到微服务上去的。 解决方案: 1、使用静态路由配置的方式,在yml配置文件...
  • 一、ERROR Filter 错误过滤器用来处理zuul异常,一般使作为打印异常堆栈、跳转异常页面、转换异常信息格式返回等操作。 Zuul已定义的错误过滤器SendErrorFilter,如果RequestContext.getThrowable()不为null,则...
  • SpringCloud Zuul网关Filter

    2019-01-08 14:23:38
    个人学习SpringCloud系列 Zuul Filter篇 Github Link: https://github.com/panjianlong13/SpringBoot-SpringCloud/tree/master/spring-cloud-zuul-filter Zuul 核心简介 Zuul 除了转发,动态路由,负载均衡等功能...
  • 接上篇《27.通过Zuul上传文件》 Spring ...本篇我们来介绍有关ZuulFilter过滤器禁用和Zuul回退的相关知识。 本部分官方文档:https://cloud.spring.io/spring-cloud-static/Finchley.SR4/single/spring-cloud.ht...
  • spring cloud-zuulFilter详解

    万次阅读 多人点赞 2017-03-15 21:05:42
    关于网关的作用,这里就不再次赘述了,我们今天的重点是zuulFilter。通过Filter,我们可以实现安全控制的,比如,只有请求参数中有用户名和密码的客户端才能访问服务端的资源。那么如果来实现Filter了? 要想实现...
  • 最近搞了搞 spring cloud的全家桶,在用到zuul的时候,有一些问题,这里记录一下 先上一段配置,根据配置来说说 spring: application: name: zuul-provider server: port: 9096 eureka: client: serviceUrl...
  • 增加pom配置: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
  • 接上篇文章:... 参考文献:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html demo代码地址:https://download.csdn.net/download/qq_33333654/...
  • zuul RequestContext filter原理

    千次阅读 2018-08-02 09:49:05
    zuul的核心是一系列的filters, 其作用可以类比Servlet框架的Filter,或者AOP。其原理就是在zuul把Request route到源web-service的时候,处理一些逻辑,比如Authentication,Load Shedding等 ==HttpRequest -&...
  • zuul网关还可以实现过滤器功能,比如进行安全控制等,网关进行统一拦截。 网关的过滤有三个阶段,1.路由被请求前 2.路由被请求时 3.路由被请求后 4.处理请求发生错误时 下面举一个简单的例子,路由请求前: 代码...
  • Spring cloud Zuul Filter 使用小经验

    千次阅读 2017-12-15 09:32:03
    今天介绍的FilterZuul中提供的,跟我们之前使用的Servlet Filter不太一样。Zuul中提供Filter的作用有哪些,我觉得分为如下几点: - 网关是暴露在外面的,必须要进行权限控制 - 可以针对服务做控制,在路由的时候...
  • Zuul的核心是一系列的过滤器,这些过滤器可以完成以下功能: 身份认证与安全:识别每个资源的验证要求,并拒绝那些与要求不符的请求。 审查与监控:在边缘位置追踪有意义的数据和统计结果,从而带来精确的生成视图...
  • 引言最近有个网友问了一个问题,zuul中如果两个filter的order一样,是如何排序的?引起了我的兴趣,特地去阅读了它的源码。zuul是干什么的如果你有使用过springcloud应该...
  • spring cloud网关zuulfilter注册

    千次阅读 2017-04-11 11:15:35
    原文地址 ...zuul开启 首先由@EnableZuulProxy 注解是开启zuul的注解。 @EnableCircuitBreaker @EnableDiscoveryClient @Target({ElementType.TYPE}) @Retention
  • 转载自:https://www.jianshu.com/p/3b7cd2af48a4 源码: @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest re...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,799
精华内容 4,319
关键字:

filterzuul