Spring Cloud - 手写 Gateway 源码,实现自定义局部 FilterFactory

这篇具有很好参考价值的文章主要介绍了Spring Cloud - 手写 Gateway 源码,实现自定义局部 FilterFactory。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、FilterFactory 分析

1.1、前置知识

1.2、分析源码

1.2.1、整体分析

1.2.2、源码分析

1.3、手写源码

1.3.1、基础框架

1.3.2、实现自定义局部过滤器

1.3.3、加参数的自定义局部过滤器器


一、FilterFactory 分析


1.1、前置知识

前面的学习我们知道,GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理,同时,springcloud 也提供了一些内置的 filter.

比如:StripPrefix,表示给请求的 url 中去表指定的 n 个前缀路由,例如 - StripPrefix=2 那么如果你原本的请求是路由是 /user/list/get ,那么经过 StripPrefix 处理后,就会变成 /get.

abstractgatewayfilterfactory,SpringCloud & Alibaba,spring cloud,gateway,spring

如果我们需要自己去实现一个像这样的局部过滤器,该怎么实现呢?

1.2、分析源码

1.2.1、整体分析

例如 StripPrefix,他继承了 AbstractGatewayFilterFactory 这个抽象类.

abstractgatewayfilterfactory,SpringCloud & Alibaba,spring cloud,gateway,spring

这里暗含了一层意思:在 application.yml 配置文件中,可以在 filters 配置里写上这个类的前缀 StripPrefix,就表示这个类(后面的 GatewayFilterFactory 是固定写法,就表示他是一个网关过滤器).

abstractgatewayfilterfactory,SpringCloud & Alibaba,spring cloud,gateway,spring

进一步的,如果我们要自定义一个局部过滤器,例如身份认证 Token 过滤器,我们就创建一个类命名为:Token + GatewayFilter,然后继承 AbstractGatewayFilterFactory 抽象类,就表示他是一个局部过滤器.

1.2.2、源码分析

源码如下:

public class StripPrefixGatewayFilterFactory extends AbstractGatewayFilterFactory<Config> {
    public static final String PARTS_KEY = "parts";

    public StripPrefixGatewayFilterFactory() {
        super(Config.class);
    }

    public List<String> shortcutFieldOrder() {
        return Arrays.asList("parts");
    }

    public GatewayFilter apply(final Config config) {
        return new GatewayFilter() {
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                ServerWebExchangeUtils.addOriginalRequestUrl(exchange, request.getURI());
                String path = request.getURI().getRawPath();
                String[] originalParts = StringUtils.tokenizeToStringArray(path, "/");
                StringBuilder newPath = new StringBuilder("/");

                for(int i = 0; i < originalParts.length; ++i) {
                    if (i >= config.getParts()) {
                        if (newPath.length() > 1) {
                            newPath.append('/');
                        }

                        newPath.append(originalParts[i]);
                    }
                }

                if (newPath.length() > 1 && path.endsWith("/")) {
                    newPath.append('/');
                }

                ServerHttpRequest newRequest = request.mutate().path(newPath.toString()).build();
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
                return chain.filter(exchange.mutate().request(newRequest).build());
            }

            public String toString() {
                return GatewayToStringStyler.filterToStringCreator(StripPrefixGatewayFilterFactory.this).append("parts", config.getParts()).toString();
            }
        };
    }

    public static class Config {
        private int parts = 1;

        public Config() {
        }

        public int getParts() {
            return this.parts;
        }

        public void setParts(int parts) {
            this.parts = parts;
        }
    }
}
  • extends AbstractGatewayFilterFactory<Config> :继承 AbstractGatewayFilterFactory 表示他是一个局部过滤器.  传入一个泛型 Config(是一个静态内部类),是因为在配置 filters 的时候,可能需要给参数指定具体的值,例如 - StripPrefix=2,而 Config 就是来处理这里的 2 这个值的.
  • public static final String PARTS_KEY = "parts": 这里就是定义一个常量,后面会用上.
  • StripPrefixGatewayFilterFactory() :构造方法,需要给父类 AbstractGatewayFilterFactory 传递 Config 参数(前面分析过了),将来在 apply 方法中会用上.
  • Config:是一个静态内部类,描述了配置 filters 时,具体要给参数指定的值,并提供了 get 和 set 方法.  这个类就需要传递给父类 AbstractGatewayFilterFactory,最后回传给 apply 方法,在 apply 方法中使用.abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring如果不想给参数指定值,就可以不写 Config 中的内容.
  • shortcutFieldOrder():这个方法是用来指定 filters 配置中参数值的顺序.  也就是说,如果 Config 个中如果有多个参数,那么你在配置 filters 时,指定参数的多个值,顺序是怎样的?他就是用来指定顺序的.abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring
  • apply(Config config):局部过滤器的核心类,用来描述过滤规则的.  这里的参数 Config 就是刚刚讲到的 静态内部类,先传递给父类,然后再回传给了 apply 方法,之后我们就可以直接在 apply 方法中去使用 Config 类中的参数.

1.3、手写源码

1.3.1、基础框架

按照上述分析,不难写出大概模样,例如我们可以模仿源码,创建包 filter.factory ,然后在这个包下定义一个 Token 局部过滤器如下:

@Component // 表示在工厂中创建对象(不能少!)
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 处理过滤逻辑......
                return chain.filter(exchange);
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return super.shortcutFieldOrder();
    }

    public static class Config {

    }

}

那么我们就可以在配置文件中,添加这个自定义的局部过滤器

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

1.3.2、实现自定义局部过滤器

例如,自定义一个 Token 局部过滤器,那么就可以创建一个类 filter.factory.TokenGatewayFilterFactory 

在 apply 中的过滤逻辑就是,判断前端是否传入 token,如果没有就抛异常,如果有就去 redis 上看看是否存在这个 token,如果存在就放行,不存在就抛异常.

@Slf4j
@Component // 表示在工厂中创建对象
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                //1.获取 token 信息
                //由于 header 中 key 可以重复(包括 parma 也是如此),因此获取到的是一个 List
                List<String> tokens = exchange.getRequest().getHeaders().get(RedisPrefix.TOKEN_KEY);
                if(tokens == null) {
                    throw new RuntimeException("没有 token 令牌!");
                }
                String tokenValue = tokens.get(0);
                log.info("token: {}", tokenValue);
                //2.比较 redis 上的 token 数据是否一致(redis 上存储的数据格式为: token前缀 + value)
                if(!redisTemplate.hasKey(RedisPrefix.TOKEN_KEY + tokenValue)) {
                    throw new RuntimeException("token 令牌不合法!");
                }
                return chain.filter(exchange);
            }
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return super.shortcutFieldOrder();
    }

    public static class Config {

    }

}

a)例如 redis 存储的数据为

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

b)执行结果如下:

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

c)如果没有 token 数据,响应如下:

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

d)如果有 token,但是 token 值错误,响应如下:

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

1.3.3、加参数的自定义局部过滤器器

如果在配置 filters 的时候,要指定一些参数,例如 isRequire(boolean类型,表示是否传),name(String 类型).

那么就可以在 Config 静态内部类中描述,然后在 shortcutFieldOrder() 方法中指定顺序,最后就可以在 apply 中拿到对应的参数,如下:

@Slf4j
@Component // 表示在工厂中创建对象
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

    @Autowired
    private StringRedisTemplate redisTemplate;

    public TokenGatewayFilterFactory() {
        super(Config.class);
    }

    /**
     * 核心方法: 处理过滤
     * @param config
     * @return
     */
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("config isRequire: {}", config.isRequire());
                log.info("config name: {}", config.getName());
                //1.拿到 Config 中自定义的参数 isRequire,判断是否要进行过滤
                if(config.isRequire()) {
                    //2.获取 token 信息
                    //由于 header 中 key 可以重复(包括 parma 也是如此),因此获取到的是一个 List
                    List<String> tokens = exchange.getRequest().getHeaders().get(RedisPrefix.TOKEN_KEY);
                    if(tokens == null) {
                        throw new RuntimeException("没有 token 令牌!");
                    }
                    String tokenValue = tokens.get(0);
                    log.info("token: {}", tokenValue);
                    //3.比较 redis 上的 token 数据是否一致(redis 上存储的数据格式为: token前缀 + value)
                    if(!redisTemplate.hasKey(RedisPrefix.TOKEN_KEY + tokenValue)) {
                        throw new RuntimeException("token 令牌不合法!");
                    }
                }
                return chain.filter(exchange);
            }
        };
    }

    /**
     * 指定参数值填写的顺序
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("require", "name");
    }

    /**
     * 提供 filter 配置中的参数值
     */
    public static class Config {
        private boolean require;
        private String name;

        public boolean isRequire() {
            return require;
        }

        public void setRequire(boolean require) {
            this.require = require;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

}

Ps:这里的不要命名为 isRequire ,会冲突! 

在配置文件中配置 filters:

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

执行结果如下:

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring

abstractgatewayfilterfactory,SpringCloud &amp; Alibaba,spring cloud,gateway,spring文章来源地址https://www.toymoban.com/news/detail-768074.html

到了这里,关于Spring Cloud - 手写 Gateway 源码,实现自定义局部 FilterFactory的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring-Cloud-Gateway如何自定义路由过滤器?

    遇到这么一个面试题:自定义网关过滤器实现把url中的请求参数放到http的header中传递给微服务。 我们知道网关的一个重要的作用就是路由转发,路由表的配置大概是这个样子: 其中的filters就是配置的路由过滤器,Spring已经内置了31个路由的过滤器,这些过滤器都是 org.spring

    2024年02月16日
    浏览(37)
  • spring cloud gateway自定义负载均衡, 优先转发到指定IP

    示例为优先将请求分配到指定IP, 如果指定IP没有服务, 则随机分配. GatewayLoadBalancerConfiguration.java 挂载配置类, 可以通过 @org.springframework.boot.autoconfigure.condition.ConditionalOnXXX 或者 @Profile 注解或者@ComponentScan过滤等方式来配置是否启用 在任意一个@Configuration注解的类上配置均可 L

    2024年02月07日
    浏览(38)
  • 【源码】Spring Cloud Gateway 是在哪里匹配路由的?

    我们知道,经过网关的业务请求会被路由到后端真实的业务服务上去,假如我们使用的是Spring Cloud Gateway,那么你知道Spring Cloud Gateway是在哪一步去匹配路由的吗? 源码之下无秘密,让我们一起从源码中寻找答案。 Spring Cloud Gateway 的入口为 DispatcherHandler 的 handle 方法,其中主

    2023年04月24日
    浏览(37)
  • 第九章 : Spring cloud 网关中心 Gateway (内置断言与自定义断言)

    第九章 : Spring cloud 网关中心 Gateway (内置断言与自定义断言) 前言 本章知识点:Spring cloud Gateway 内置断言(Predicate)类型的 yaml 和 json 数据格式;自定义路由Predicate 断言的案例。 Gateway 内置 Predicate 格式配置 概述 本文针对 Spring Cloud Gateway 框架中断言(Predicate)类型在 Yaml

    2024年01月17日
    浏览(36)
  • Spring Cloud Gateway 实现原理

    Spring Cloud Gateway是Spring Cloud生态系统中的一个组件,用于构建基于Spring Boot的微服务架构中的网关服务。它的主要目的是提供一种灵活的方式来路由、过滤和转换HTTP请求,从而允许您构建强大、高性能的微服务应用程序。 以下是Spring Cloud Gateway的一些核心原理和功能: 路由(

    2024年02月10日
    浏览(38)
  • Spring Cloud GateWay实现熔断降级

    当分布式系统中的网关接收到大量请求并向后端远程系统或服务发起调用时,后端服务可能会产生调用失败(如超时或异常)。这时,如果让请求继续堆积在网关上,可能会导致整个系统的瘫痪。因此,需要快速失败并返回请求,这就是所谓的熔断。 降级是指在系统资源不足

    2024年02月02日
    浏览(39)
  • Spring Cloud Gateway实现灰度发布功能

    灰度发布又名金丝雀发布,在微服务中的表现为同一服务同时上线不同版本,让一部分用户使用新版本来验证新特性,如果验证没有问题,则将所有用户都迁移到新版本上。 在微服务架构中,网关负责请求的统一入口,主要功能之一是请求路由。而灰度发布实质就是让指定用

    2024年02月21日
    浏览(80)
  • Spring Cloud Gateway 网关实现白名单功能

    1 摘要 对于微服务后台而言,网关层作为所有网络请求的入口。一般基于安全考虑,会在网关层做权限认证,但是对于一些例如登录、注册等接口以及一些资源数据,这些是不需要有认证信息,因此需要在网关层设计一个白名单的功能。本文将基于 Spring Cloud Gateway 2.X 实现白

    2023年04月08日
    浏览(42)
  • Spring Cloud Gateway编码实现任意地址跳转

    这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 作为《Spring Cloud Gateway实战》系列的第十四篇,本文会继续发掘Spring Cloud Gateway的潜力,通过编码体验操控网关的乐趣,开发出一个实用的功能:让Spring Cloud Gateway应用在收到请求后,可以按照业务的

    2024年02月11日
    浏览(40)
  • Spring Cloud Gateway集成Nacos实现负载均衡

    💡Nacas可以用于实现Spring Cloud Gateway中网关动态路由功能,也可以基于Nacos来实现对后端服务的负载均衡,前者利用Nacos配置中心功能,后者利用Nacos服务注册功能。 接下来我们来看下Gateway集成Nacos实现负载均衡的架构图 一. 环境准备 1. 版本环境 Jdk: java.version1.8/java.version Spr

    2024年02月10日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包