Spring Cloud / Alibaba 微服务架构 | 2021年11月更文挑战(26)

574 阅读3分钟

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

上篇文章我们介绍了SpringCloud Gateway Filter的相关概念,并解读了一个全局过滤器——RouteToRequestUrlFilter的filter方法,这篇文章我们来介绍一下两个局部过滤器。

解读SpringCloud Gateway Filter(下)

全局过滤器是实现GlobalFilter, Ordered接口,局部过滤器是实现GatewayFilter, Ordered接口,然后再去实现对应的apply方法。

1、添加前缀的局部过滤器 PrefixPathGatewayFilterFacotry

可以看出来,这是一个局部过滤器的工厂,在工厂内实现了一个局部过滤器。有一个属性 PREFIX_KEY,是指明需要在url上增加的前缀。

接下来看下代码实现。

public GatewayFilter apply(PrefixPathGatewayFilterFactory.Config config) {
    return new GatewayFilter() {
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            boolean alreadyPrefixed = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR, false);
            if (alreadyPrefixed) {
                return chain.filter(exchange);
            } else {
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ALREADY_PREFIXED_ATTR, true);
                ServerHttpRequest req = exchange.getRequest();
                ServerWebExchangeUtils.addOriginalRequestUrl(exchange, req.getURI());
                String newPath = config.prefix + req.getURI().getRawPath();
                ServerHttpRequest request = req.mutate().path(newPath).build();
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, request.getURI());
                if (PrefixPathGatewayFilterFactory.log.isTraceEnabled()) {
                    PrefixPathGatewayFilterFactory.log.trace("Prefixed URI with: " + config.prefix + " -> " + request.getURI());
                }

                return chain.filter(exchange.mutate().request(request).build());
            }
        }

        public String toString() {
            return GatewayToStringStyler.filterToStringCreator(PrefixPathGatewayFilterFactory.this).append("prefix", config.getPrefix()).toString();
        }
    };
}

alreadyPrefixed是boolean类型,用来校验一下是否已经添加前缀了,通过exchange.getAttributeOrDefault中的GATEWAY_ALREADY_PREFIXED_ATTR属性来判断,默认值为false。

接下来使用config的Prefix去构造新的路径,新的HttpRequest。先根据exchange.getRequest()获取请求对应的request,然后根据request获取到对应的url,然后在url上去添加一个前缀生成新的path,用这个新的path去构造新的ServerHttpRequest。

举个例子,假如有个简单的静态路由配置如下:

spring:
    cloud:
        routes:
            -id: sheep
            uri: http//example/org
            filters:
                - PrefixPath=/mypath

假如现在有个请求/hello,命中了上方的配置,那么这个添加前缀的局部过滤器就会将这个请求变成:http://example/org/mypath/hello。

2、去除前缀的局部过滤器 StripPrefixGatewayFilterFactory

strip是跳过的意思,所以该过滤器是用来去掉前缀的。

public GatewayFilter apply(StripPrefixGatewayFilterFactory.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 newPath = "/" + (String) Arrays.stream(StringUtils.tokenizeToStringArray(path, "/")).skip((long)config.parts).collect(Collectors.joining("/"));
            newPath = newPath + (newPath.length() > 1 && path.endsWith("/") ? "/" : "");
            ServerHttpRequest newRequest = request.mutate().path(newPath).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();
        }
    };
}

通过request.getURI().getRawPath()去获取原始的URI,然后根据配置的parts部分去执行.skip(config.parts)去掉前缀。parts是int类型,也就是指定一个数字,去掉几个前缀的部分。

然后将获取到的新path去构造一个新的request再进行转发。

举个例子,假如有个简单的静态路由配置如下:

spring:
    cloud:
        routes:
            -id: sheep
            uri: http//example/org
            predicates:
                - Path=/name/**
            filters:
                - StripPrefix=2

如果这时候有个请求是/name/bar/foo命中了上方的配置,那么这个请求就会变成/foo,要注意的是,这个stripPrefix=2指的是去掉两个"/xxx",而不是字符!

SpringCloud Gateway Filter 的执行流程

1、过滤器有优先级之分,Order越大,优先级就越低,越晚被执行。可以通过getOrder方法获取Order。

2、全局过滤器所有的请求都会执行,这是默认的行为,不需要做额外的操作,针对自定义的全局过滤器也是如此。

3、局部过滤器只有配置的请求才会执行,配置在路由配置中,之后文章会介绍如何配置才会使其生效。

自定义局部过滤器的步骤

1、自定义局部过滤器实现GatewayFilter, Ordered 这两个接口中的相应方法。

2、将自定义的局部过滤器加入到过滤器工厂中,并将工厂加入到Spring IOC容器中,工厂继承 AbstractGatewayFilterFactory 抽象类。

3、配置文件中配置过滤器,使其生效。