Spring Cloud Gateway - Extended

[TOC]


Spring Cloud Gateway Monitoring endpoint

When it comes to surveillance, you should be able to think of Spring Boot Actuator. And based on Actuator Spring Cloud Gateway provides a number of monitors endpoints. Just add the project spring-boot-starter-actuatordependent and gatewayexposed endpoints, you can get a number of monitors endpoints. Configuration example:

management:
  endpoints:
    web:
      exposure:
        include: gateway  # 或者配置“*”暴露全部端点

Spring Cloud Gateway Monitoring endpoint in the following table:

Endpoint Request method description
globalfilters GET Show all global filter information
routefilters GET Show all the information filter factory
refresh The POST (no message body) Empty route cache that refresh routing information
routes GET Display a list of all the routing information
routes/{id} GET Id specified display routing information
routes/{id} The POST (with a message body) A new route
routes/{id} The DELETE (no message body) Delete a route

All Gateway monitors endpoints are mounted at /actuator/gatewaythe lower path, such as globalfiltersthe full access path endpoint is /actuator/gateway/globalfilters. The main endpoint is to look at what Gateway enabled global filters and their order of execution (the smaller the number the more precedence). So when we do not know what Gateway enables a global filter, or do not know the sequence in which global filters, you can access the endpoint view:
Spring Cloud Gateway - Extended

Similarly, if you do not know what Gateway enables a filter factory, you can access the routefiltersendpoint Views:
Spring Cloud Gateway - Extended

If you want to know which route defined in Gateway do not want to view the configuration file, then you can routessee a list of all endpoints of routing information:
Spring Cloud Gateway - Extended

If the routing configuration to customize the appearance and behavior do not take effect or does not match expectations, then you can routes/{id}view the details of the route specific endpoint:
Spring Cloud Gateway - Extended

routes/{id}Endpoint may also be used to add dynamic routing, POST requests and simply send the body to define a message. Example message body:

{
  "predicates": [
    {
      "name": "Path",
      "args": {
        "_genkey_0": "/test"
      }
    }
  ],
  "filters": [
    {
      "name": "AddRequestHeader",
      "args": {
        "_genkey_0": "X-Request-Foo",
        "_genkey_1": "Bar"
      }
    },
    {
      "name": "PreLog",
      "args": {
        "_genkey_0": "a",
        "_genkey_1": "b"
      }
    }
  ],
  "uri": "https://www.example.com",
  "order": 0
}

In fact, the message body is regular, you can configure in the configuration file a routing rule, and then access the routesendpoint route_definitionfield is the content of the message body, as follows:
Spring Cloud Gateway - Extended

接下来我们实际测试一下,复制该消息体,然后稍微修改一下并进行发送,如下:
Spring Cloud Gateway - Extended

路由添加成功后,访问 routes 端点,就可以看到新添加的路由:
Spring Cloud Gateway - Extended

注:如果没有实时生效,使用 refresh 端点刷新一下路由信息即可

官方文档:


关于Gateway的调试、排错

1、Gateway的监控端点:

上一小节介绍了Gateway的监控端点,这些监控端点可以帮助我们分析全局过滤器、过滤器工厂、路由详情等

2、日志:

设置一些相关包的日志级别,打印更详细的日志信息,可按需将如下包的日志级别设置成 debugtrace

  • org.springframework.cloud.gateway
  • org.springframework.http.server.reactive
  • org.springframework.web.reactive
  • org.springframework.boot.autoconfigure.web
  • reactor.netty
  • redisratelimiter

配置示例:

logging:
  level:
    org.springframework.cloud.gateway: trace

3、Wiretap Logger【需Greenwich SR3及更高版本才会支持】:

Reactor Netty的 HttpClient 以及 HttpServer 可启用 Wiretap。需将 reactor.netty 包设置成 debugtrace ,然后在配置文件中添加如下配置:

  • spring.cloud.gateway.httpserver.wiretap=true:开启 HttpServer 的Wiretap
  • spring.cloud.gateway.httpclient.wiretap=true:开启 HttpClient 的Wiretap

wiretap其实是Reactor Netty的概念,用于打印对端之间的流量详情,相关文档:


过滤器执行顺序

我们都知道全局过滤器使用@Order 注解或实现 Ordered 接口来配置一个决定执行顺序的数字,该数字越小的过滤器越靠前执行。

但是在路由规则上所配置的过滤器工厂并没有配置类似Order之类的东西,那么是如何决定执行顺序的呢?其实,过滤器工厂默认也会被设置一个Order,该Order按配置顺序从1开始递增,也是Order越小越靠前执行。如下:

routes:
  - id: test-route
    uri: lb://user-center
    predicates:
      - TimeBetween=上午9:00,下午5:00
    filters:
      # 按配置顺序从1开始递增
      - AddRequestHeader=Y-Header, Bar    # Order为1
      - AddResponseHeader=X-Header, Bar   # Order为2
      - PreLog=testName,testValue         # Order为3

使用default-filters配置的默认过滤器也是同理,但如果配置了默认过滤器,则会先执行相同Order的默认过滤器:

default-filters: 
  - AddRequestHeader=Y-Foo, Bar    # Order为1
  - AddResponseHeader=X-Foo, Bar   # Order为2
routes:
  - id: test-route
    uri: lb://user-center
    predicates:
      - TimeBetween=上午9:00,下午5:00
    filters:
      # 按配置顺序从1开始递增
      - AddRequestHeader=Y-Header, Bar    # Order为1
      - AddResponseHeader=X-Header, Bar   # Order为2
      - PreLog=testName,testValue         # Order为3

如需自行控制过滤器工厂的Order,可返回OrderedGatewayFilter,如下示例:

@Slf4j
@Component
public class PreLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public OrderedGatewayFilter apply(NameValueConfig config) {
        return new OrderedGatewayFilter((exchange, chain) -> {
            ...
            return chain.filter(exchange);
        }, -1);
    }
}

核心代码:

  • org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#loadGatewayFilters:为过滤器设置了Order 数值,从1开始
  • org.springframework.cloud.gateway.route. RouteDefinitionRouteLocator#getFilters:加载默认过滤器 & 路由过滤器,并对过滤器做了排序
  • org.springframework.cloud.gateway.handler.FilteringWebH .andler#handle:构建过滤器链并执行

Spring Cloud Gateway跨域配置

Gateway支持CORS相关配置,可以通过不同的URL规则匹配不同的CORS策略。配置示例:

spring:
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "http://docs.spring.io"
            allowedMethods:
            - GET

除此之外,还可以通过自定义过滤器来解决跨域问题,具体代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

Spring Cloud Gateway限流相关

在高并发的系统中,限流往往是一个绕不开的话题,我们都知道网关是流量的入口,所以在网关上做限流也是理所当然的。Spring Cloud Gateway内置了一个过滤器工厂,用于提供限流功能,这个过滤器工厂就是是RequestRateLimiterGatewayFilterFactory,该过滤器工厂基于令牌桶算法实现限流功能。

目前,该过滤器工厂默认使用 RedisRateLimiter 作为限速器,需要依赖Redis来存储限流配置,以及统计数据等。当然你也可以实现自己的RateLimiter,只需实现 org.springframework.cloud.gateway.filter.ratelimit.RateLimiter 接口,或者继承 org.springframework.cloud.gateway.filter.ratelimit.AbstractRateLimiter抽象类

Tips:

Redis Rate Limiter的实现基于这篇文章:Scaling your API with rate limiters
Spring官方引用的令牌桶算法文章:Token bucket

关于令牌桶之类的限流算法可以参考另一篇文章,这里就不过多赘述了:


动手实践

1、添加Redis依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2、添加如下配置:

spring:
  cloud:
    gateway:
      routes:
        - id: user-center
          uri: lb://user-center
          predicates:
            - Path=/user-center/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 2
                # 使用SpEL表达式从Spring容器中获取Bean对象
                key-resolver: "#{@pathKeyResolver}"
  # redis相关              
  redis:
    host: 127.0.0.1
    port: 6379

3, a write KeyResolver, for what were used to define the current limit. For example, in accordance with current limiting access path, wrote one for the access path KeyResolver; limiting parameters in accordance with the request, it would write a request parameters KeyResolver, and so on. Here we are limiting the access path in accordance with the specific codes are as follows:

@Configuration
public class RaConfiguration {

    /**
     * 按照Path限流
     *
     * @return key
     */
    @Bean
    public KeyResolver pathKeyResolver() {
        return exchange -> Mono.just(
                exchange.getRequest()
                        // 获取path
                        .getPath()
                        .toString()
        );
    }
}

It is obvious from the code is implemented, actually just a return to the access path, which will limit the role of the access path to flow rules. For example access: http://${GATEWAY_URL}/users/1For this path, it's redis-rate-limiter.replenishRate = 1, redis-rate-limiter.burstCapacity = 2.

Access: http://${GATEWAY_URL}/shares/1For this path, it's redis-rate-limiter.replenishRate = 1, redis-rate-limiter.burstCapacity = 2; and so on ......

test

Then perform a simple test to see whether the current limit takes effect. Frequent access to a continuous path, when the token bucket token is consumed over, it will return 429the HTTP status code. as follows:
Spring Cloud Gateway - Extended

Then quickly see Redis stored in the key, will find its format is as follows:
Spring Cloud Gateway - Extended

The format can be seen from the key out, in fact, KeyResolverthe purpose is to obtain a unique identifier of a request (this identification can be access path, may be a request parameter, in short, is that we can get out of this thing in the request), and with and an analysis of its key generation key, in order to achieve targeted limiting.

expand

If the request carries a named userparameter, which is a user name, then we can achieve flow limit for a user's request by this parameter. as follows:

@Bean
public KeyResolver userKeyResolver() {
    return exchange -> Mono.just(
        exchange.getRequest()
            .getQueryParams()
            .getFirst("user")
    );
}

Similarly, we can also be limiting for the source IP requests. as follows:

@Bean
public KeyResolver ipKeyResolver() {
    return exchange -> Mono.just(
      exchange.getRequest()
          .getHeaders()
          .getFirst("X-Forwarded-For")
    );
}

Guess you like

Origin blog.51cto.com/zero01/2430532