前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloud Gateway中你不知道的骚操作

SpringCloud Gateway中你不知道的骚操作

作者头像
Java学习录
发布2019-12-03 16:35:55
1.8K0
发布2019-12-03 16:35:55
举报
文章被收录于专栏:Java学习录

之前的几篇文章中,我们已经提到了如何使用SpringCloud Gateway,那几篇文章的内容已经足够做普通项目使用了,但是如果你想深入了解这个东西,或者说是看完这篇文章你用起来跟普通人就完全不是一个等级的了

路由谓语工厂的骚操作
AfterRoutePredicateFactory

前置时间路由工厂,匹配当前时间发生后的请求

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
BeforeRoutePredicateFactory

后置时间路由工厂,匹配当前时间发生之前的请求

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
BetweenRoutePredicateFactory

匹配时间段的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://example.org
        predicates:
        - Betweeen=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

这个谓语工厂的判断逻辑如下:

代码语言:javascript
复制
 public Predicate<ServerWebExchange> apply(Config config) {
        ZonedDateTime datetime1 = getZonedDateTime(config.datetime1);
        ZonedDateTime datetime2 = getZonedDateTime(config.datetime2);
        Assert.isTrue(datetime1.isBefore(datetime2),
                config.datetime1 +
                " must be before " + config.datetime2);
        return exchange -> {
            final ZonedDateTime now = ZonedDateTime.now();
            return now.isAfter(datetime1) && now.isBefore(datetime2);
        };
    }
CookieRoutePredicateFactory

匹配请求cookie的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://example.org
        predicates:
        - Cookie=chocolate, ch.p

判断逻辑如下:

代码语言:javascript
复制
 public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);
            if (cookies == null) {
                return false;
            }
            for (HttpCookie cookie : cookies) {
                if (cookie.getValue().matches(config.regexp)) {
                    return true;
                }
            }
            return false;
        };
    }
HeaderRoutePredicateFactory

匹配请求header的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://example.org
        predicates:
        - Header=X-Request-Id, \d+

实现逻辑如下:

代码语言:javascript
复制
  public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            List<String> values = exchange.getRequest().getHeaders().get(config.header);
            if (values != null) {
                for (String value : values) {
                    if (value.matches(config.regexp)) {
                        return true;
                    }
                }
            }
            return false;
        };
    }
HostRoutePredicateFactory

匹配请求host的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Host=**.somehost.org

实现逻辑如下:

代码语言:javascript
复制
  public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String host = exchange.getRequest().getHeaders().getFirst("Host");
            return this.pathMatcher.match(config.getPattern(), host);
        };
    }
MethodRoutePredicateFactory

匹配请求method的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET

实现逻辑如下:

代码语言:javascript
复制
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            HttpMethod requestMethod = exchange.getRequest().getMethod();
            return requestMethod == config.getMethod();
        };
    }
PathRoutePredicateFactory

匹配请求路径的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://example.org
        predicates:
        - Path=/foo/{segment}

实现逻辑如下:

代码语言:javascript
复制
    public Predicate<ServerWebExchange> apply(Config config) {
        synchronized (this.pathPatternParser) {
            config.pathPattern = this.pathPatternParser.parse(config.pattern);
        }
        return exchange -> {
            PathContainer path = parsePath(exchange.getRequest().getURI().getPath());

            boolean match = config.pathPattern.matches(path);
            traceMatch("Pattern", config.pathPattern.getPatternString(), path, match);
            if (match) {
                PathMatchInfo uriTemplateVariables = config.pathPattern.matchAndExtract(path);
                exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
                return true;
            } else {
                return false;
            }
        };
    }
QueryRoutePredicateFactory

匹配请求参数的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://example.org
        predicates:
        - Query=foo, ba.

实现逻辑如下:

代码语言:javascript
复制
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            if (!StringUtils.hasText(config.regexp)) {
                // check existence of header
                return exchange.getRequest().getQueryParams().containsKey(config.param);
            }
            List<String> values = exchange.getRequest().getQueryParams().get(config.param);
            for (String value : values) {
                if (value.matches(config.regexp)) {
                    return true;
                }
            }
            return false;
        };
    }
RemoteAddrRoutePredicateFactory

匹配远程地址的路由谓语工厂

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: http://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

实现逻辑如下:

代码语言:javascript
复制
    public Predicate<ServerWebExchange> apply(Config config) {
        List<IpSubnetFilterRule> sources = convert(config.sources);

        return exchange -> {
            InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
            if (remoteAddress != null) {
                String hostAddress = remoteAddress.getAddress().getHostAddress();
                String host = exchange.getRequest().getURI().getHost();

                if (log.isDebugEnabled() && !hostAddress.equals(host)) {
                    log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
                }

                for (IpSubnetFilterRule source : sources) {
                    if (source.matches(remoteAddress)) {
                        return true;
                    }
                }
            }

            return false;
        };
    }
拦截器的骚操作
AddRequestHeaderGatewayFilterFactory

为请求中添加header的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

实现逻辑如下:

代码语言:javascript
复制
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest().mutate()
                    .header(config.getName(), config.getValue())
                    .build();

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

为请求中添加请求参数的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: http://example.org
        filters:
        - AddRequestParameter=foo, bar

实现逻辑如下:

代码语言:javascript
复制
    public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            URI uri = exchange.getRequest().getURI();
            StringBuilder query = new StringBuilder();
            String originalQuery = uri.getRawQuery();

            if (StringUtils.hasText(originalQuery)) {
                query.append(originalQuery);
                if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
                    query.append('&');
                }
            }

            //TODO urlencode?
            query.append(config.getName());
            query.append('=');
            query.append(config.getValue());

            try {
                URI newUri = UriComponentsBuilder.fromUri(uri)
                        .replaceQuery(query.toString())
                        .build(true)
                        .toUri();

                ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();

                return chain.filter(exchange.mutate().request(request).build());
            } catch (RuntimeException ex) {
                throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
            }
        };
    }
AddResponseHeaderGatewayFilterFactory

为响应中添加header的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

实现逻辑如下:

代码语言:javascript
复制
public GatewayFilter apply(NameValueConfig config) {
        return (exchange, chain) -> {
            exchange.getResponse().getHeaders().add(config.getName(), config.getValue());

            return chain.filter(exchange);
        };
    }
HystrixGatewayFilterFactory

服务熔断的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: hytstrix_route
        uri: http://example.org
        filters:
        - Hystrix=myCommandName
          args:
            name: default
            fallbackUri: forward:/defaultfallback

实现逻辑如下,建议搭配之前的Hystrix源码解析阅读

代码语言:javascript
复制
    public GatewayFilter apply(Config config) {
        //TODO: if no name is supplied, generate one from command id (useful for default filter)
        if (config.setter == null) {
            Assert.notNull(config.name, "A name must be supplied for the Hystrix Command Key");
            HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName());
            HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(config.name);

            config.setter = Setter.withGroupKey(groupKey)
                    .andCommandKey(commandKey);
        }

        return (exchange, chain) -> {
            RouteHystrixCommand command = new RouteHystrixCommand(config.setter, config.fallbackUri, exchange, chain);

            return Mono.create(s -> {
                Subscription sub = command.toObservable().subscribe(s::success, s::error, s::success);
                s.onCancel(sub::unsubscribe);
            }).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
                if (throwable instanceof HystrixRuntimeException) {
                    HystrixRuntimeException e = (HystrixRuntimeException) throwable;
                    if (e.getFailureType() == TIMEOUT) { //TODO: optionally set status
                        setResponseStatus(exchange, HttpStatus.GATEWAY_TIMEOUT);
                        return exchange.getResponse().setComplete();
                    }
                }
                return Mono.error(throwable);
            }).then();
        };
    }
PrefixPathGatewayFilterFactory

为请求路径添加前缀的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - PrefixPath=/mypath

实现逻辑如下:

代码语言:javascript
复制
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {

            boolean alreadyPrefixed = exchange.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
            if (alreadyPrefixed) {
                return chain.filter(exchange);
            }
            exchange.getAttributes().put(GATEWAY_ALREADY_PREFIXED_ATTR, true);

            ServerHttpRequest req = exchange.getRequest();
            addOriginalRequestUrl(exchange, req.getURI());
            String newPath = config.prefix + req.getURI().getRawPath();

            ServerHttpRequest request = req.mutate()
                    .path(newPath)
                    .build();

            exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());

            if (log.isTraceEnabled()) {
                log.trace("Prefixed URI with: "+config.prefix+" -> "+request.getURI());
            }

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

请求限流的拦截器

这个拦截器相比于上方的各个拦截器还是稍微复杂一点的。首先需要引入一个额外的包

代码语言:javascript
复制
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

然后呢你要基于SpringBoot做好redis的自动装配,紧接着就是设置限流key,限流key的设置需要实现keyResolver接口,然后通过它的这个方法Mono resolve(ServerWebExchange exchange)返回你需要设置的限流key

配置的地方是这样的

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: requestratelimiter_route
        uri: http://example.org
        filters:
        - RequestRateLimiter=10, 20, #{@userKeyResolver}

其中前两个参数分别是令牌桶算法的两个参数,填充速率和限流阈值,更多关于限流算法的操作可以参考我的这篇文章:大型网站限流算法的实现和改造

拦截器实现逻辑如下:

代码语言:javascript
复制
    public GatewayFilter apply(Config config) {
        KeyResolver resolver = (config.keyResolver == null) ? defaultKeyResolver : config.keyResolver;
        RateLimiter<Object> limiter = (config.rateLimiter == null) ? defaultRateLimiter : config.rateLimiter;

        return (exchange, chain) -> {
            Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);

            return resolver.resolve(exchange).flatMap(key ->
                    // TODO: if key is empty?
                    limiter.isAllowed(route.getId(), key).flatMap(response -> {

                        for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
                            exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
                        }

                        if (response.isAllowed()) {
                            return chain.filter(exchange);
                        }

                        exchange.getResponse().setStatusCode(config.getStatusCode());
                        return exchange.getResponse().setComplete();
                    }));
        };
    }
RedirectToGatewayFilterFactory

请求重定向的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: prefixpath_route
        uri: http://example.org
        filters:
        - RedirectTo=302, http://acme.org

实现逻辑如下:

代码语言:javascript
复制
    public GatewayFilter apply(String statusString, String urlString) {
        final HttpStatus httpStatus = parse(statusString);
        Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString);
        final URL url;
        try {
            url = URI.create(urlString).toURL();
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("Invalid url " + urlString, e);
        }
        return apply(httpStatus, url);
    }
RemoveRequestHeaderGatewayFilterFactory/RemoveResponseHeaderGatewayFilterFactory

删除请求、响应中的header拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: http://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

实现逻辑这里就贴其中一个了:

代码语言:javascript
复制
    public GatewayFilter apply(NameConfig config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest().mutate()
                    .headers(httpHeaders -> httpHeaders.remove(config.getName()))
                    .build();

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

重写请求路径的拦截器

代码语言:javascript
复制
spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: http://example.org
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/

实现逻辑如下:

代码语言:javascript
复制
  public GatewayFilter apply(Config config) {
        String replacement = config.replacement.replace("$\\", "$");
        return (exchange, chain) -> {
            ServerHttpRequest req = exchange.getRequest();
            addOriginalRequestUrl(exchange, req.getURI());
            String path = req.getURI().getRawPath();
            String newPath = path.replaceAll(config.regexp, replacement);

            ServerHttpRequest request = req.mutate()
                    .path(newPath)
                    .build();

            exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());

            return chain.filter(exchange.mutate().request(request).build());
        };
    }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java学习录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 路由谓语工厂的骚操作
    • AfterRoutePredicateFactory
      • BeforeRoutePredicateFactory
        • BetweenRoutePredicateFactory
          • CookieRoutePredicateFactory
            • HeaderRoutePredicateFactory
              • HostRoutePredicateFactory
                • MethodRoutePredicateFactory
                  • PathRoutePredicateFactory
                    • QueryRoutePredicateFactory
                      • RemoteAddrRoutePredicateFactory
                      • 拦截器的骚操作
                        • AddRequestHeaderGatewayFilterFactory
                          • AddRequestParameterGatewayFilterFactory
                            • AddResponseHeaderGatewayFilterFactory
                              • HystrixGatewayFilterFactory
                                • PrefixPathGatewayFilterFactory
                                  • RequestRateLimiterGatewayFilterFactory
                                    • RedirectToGatewayFilterFactory
                                      • RemoveRequestHeaderGatewayFilterFactory/RemoveResponseHeaderGatewayFilterFactory
                                        • RewritePathGatewayFilterFactory
                                        领券
                                        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档