开发者

SpringCloud Gateway的路由,过滤器和限流解读

目录
  • Spring Cloud Gateway
    • predicates路由断言工厂
  • 全局过滤器
    • fGatewayFilter工厂
      • filters配置
      • Hystrix GatewayFilter工厂
      • 限流RequestRateLimiter GatewayFilter工厂
      • 参考文档
    • 总结

      Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?

      Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。

      Spring Cloud Gateway

      Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

      注意:

      Spring Cloud Gateway是基于Spring Boot 2.x, Spring WebFlux和Projec开发者_JAVA开发t Reactor 构建的。

      因此,在使用Spring Cloud Gateway时,许多不熟悉的同步库(例如,Spring Data和Spring Security)和模式可能不适用。

      如果您对这些项目不熟悉,建议您在使用Spring Cloud Gateway之前先阅读它们的文档,以熟悉一些新概念。

      Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时。它不能在传统的Servlet容器中或作为WAR构建。

      Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

      SpringCloud Gateway的路由,过滤器和限流解读

      Spring Cloud Gateway 的特征:

      • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
      • 动态路由
      • Predicates 和 Filters 作用于特定路由
      • 集成 Hystrix 断路器
      • 集成 Spring Cloud DiscoveryClient
      • 易于编写的 Predicates 和 Filters
      • 限流
      • 路径重写

      相关概念:

      • Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
      • Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
      • Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。

      Spring Cloud Gateway 网关路由有两种配置方式:

      • 在配置文件 yml 中配置通过@Bean自定义 RouteLocator,在启动主类 Application 中配置
      • 这两种方式是等价的,建议使用 yml 方式进配置。

      pom.XML

      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.0.6.RELEASE</version>
          <relativePath/> <!-- lookup parent from repository -->
      </parent>
      
      <dependencyManagement>
          <dependencies>
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-dependencies</artifactId>
            http://www.devze.com      <version>Finchley.SR2</version>
                  <type>pom</type>
                  <scope>import</scope>
              </dependency>
          </dependencies>
      </dependencyManagement>
      
      <dependencies>
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-gateway</artifactId>
      </dependency>
      </dependencies>
      

      application.yml

      server:
        port: 8080
      spring:
        cloud:
          gateway:
            routes:
             - id: bamboo_route
              uri: https://blog.csdn.net/
              predicates:
              - Path=/blogdevteam
      
      

      yml方式配置路由

      • id:我们自定义的路由 ID,保持唯一
      • uri:目标服务地址
      • predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
      • filters:过滤规则,本示例暂时没用。

      启动类:代码方式配置路由

      package com.bamboo;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.gateway.route.RouteLocator;
      import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
      import org.springframework.context.annotation.Bean;
      
      /**
       * @program: spring-gateway
       * @description: Application
       * @author: Bamboo zjcjava@163.com
       * @create: 2019-10-26 20:00
       **/
      @SpringBootApplication
      public class Application {
          @Bean
          public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
              return builder.routes()
                      // basic proxy
                      .route(r -> r.path("/zjcjava")
                              .uri("https://blog.csdn.net//"))
                      .build();
          }
      
          public static void main(String[] args) {
              SpringApplication.run(Application.class, args);
          }
      
      }

      http://localhost:8080/zjcjava

      对应访问的地址是

      https://blog.csdn.net/zjcjava

      http://localhost:8080/blogdevteam

      对应访问的地址是

      https://blog.csdn.net/blogdevteam

      可以看出来,它会自动在url路径/后面加上对应的路由地址

      predicates路由断言工厂

      Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分进行匹配。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些谓词都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以合并,也可以通过逻辑合并and。

      Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。

      在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。

      SpringCloud Gateway的路由,过滤器和限流解读

      spring:
        cloud:
          gateway:
              discovery:
                locator:
                enabled: true
                lower-case-service-id: true ##会使用serviceId转发到具体的服务IP上的服务
            routes:
            - id: after_route
              uri: https://example.org
              predicates:
              - After=2017-01-20T17:42:47.789-07:00[America/Denver]
              - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
              - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
              - Cookie=chocolate, ch.p
              - Header=X-Request-Id, \d+
              - Host=**.somehost.org,**.anotherhost.org
              - Method=GET
              - Path=/foo/{segment},/bar/{segment}
              - Query=baz
              - RemoteAddr=192.168.1.1/24
            - id: weight_high
              uri: https://weighthigh.org
              predicates:
              - Weight=group1, 8
            - id: weight_low
              uri: https://weightlow.org
              predicates:
              - Weight=group1, 2
      

      spring.cloud.gateway.discovery.locator.enabled:使用eurake注册服务后:是否与服务发现组件ID进行结合,通过routes中的serviceId 转发python到具体的服务实例。

      默认为false,设为true便开启通过服务中心的自动根据 serviceId 创建路由的功能。

      • 后路线断言工厂:After=2017年1月20日17:42山区时间(丹佛)之后的所有请求匹配
      • 路线断言工厂之前:Before=2017年1月20日17:42山区时间(丹佛)之前的所有请求匹配。
      • 路线断言工厂之间- Between=2017年1月20日山区时间(丹佛)之后和2017年1月21日17:42山区时间(丹佛)之后的所有请求匹配
      • Cookie路线断言工厂 Cookie=请求匹配一个名为chocolatewho的值为ch.p正则表达式匹配的cookie
      • 标头路由断言工厂 - Header=匹配请求具有名为X-Request-Id其值为\d+正则表达式匹配(具有一个或多个数字的值)的标头
      • 主机路由断言工厂 - Host=请求的Host标头中包含值www.somehost.org或beta.somehost.org,则此路由将匹配www.anotherhost.org
      • 方法路线断言工厂 - Method=此路由将匹配GET方式的请求
      • 路径路线断言工厂 - Path=将匹配:/foo/1或/foo/bar或/bar/baz
      • 查询路由断言工厂 - Query=匹配请求包含baz查询参数
      • RemoteAddr路由断言工厂 - RemoteAddr=请求的远程地址为192.168.1.1/24之间的IP,则此路由将匹配192.168.1.10
      • 权重路线断言工厂 将约80%的流量转发至weighthigh.org,并将约20%的流量转发至weightlow.org

      全局过滤器

      该GlobalFilter接口具有与相同的签名GatewayFilter。这些是特殊过滤器,有条件地应用于所有路由。(此界面和用法可能会在将来的里程碑中更改)。

      全局过滤器和GatewayFilter的组合订购

      当请求进入(并与路由匹配)时,过滤Web处理程序会将的所有实例GlobalFilter和所有特定GatewayFilter于路由的实例添加到过滤器链中。该组合的过滤器链按org.springframework.core.Ordered接口排序,可以通过实现该getOrder()方法进行设置。

      由于Spring Cloud Gateway区分了执行过滤器逻辑的“前”阶段和“后”阶段(请参阅:工作原理),因此优先级最高的过滤器将在“前”阶段中处于第一个阶段,而在“后”阶段中处于最后一个阶段“-相。

      ExampleConfiguration.java

      //放入application启动类中main方法后面即可
      @Bean
      public CustomGlobalFilter tokenFilter() {
          return new CustomGlobalFilter();
      }
      
      
      
      /**
       * @program: spring-gateway
       * @description: 全局过滤器
       * @author: Bamboo zjcjava@163.com
       * @create: 2019-10-26 23:06
       **/
      public class CustomGlobalFilter implements GlobalFilter, Ordered {
      
          private static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class);
          @Override
          public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
              log.info("custom global filter.....................................");
              // 添加全局鉴权
              String token = exchange.getRequest().getQueryParams().getFirst("token");
              if (token == null || token.isEmpty()) {
                  exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                  return exchange.getResponse().setComplete();
              }
      
              return chain.filter(exchange);
          }
      
          @Override
          public int getOrder() {
              return -1;
          }
      }
      

      这里我创建了一个全局的鉴权过滤器,只有参数中带有token值才能继续,否则提示无权访问

      http://localhost:8080/zjcjava?token=aa

      fGatewayFilter工厂

      路由过滤器允许以某种方式修改传入的HTTP请求或传出的HTTP响应。路由过滤器适用于特定路由。Spring Cloud Gateway包括许多内置的GatewayFilter工厂。

      filters配置

      修改配置文件如下:抓哟是 path,filters

      server:
        port: 8080
      spring:
        application:
          name: spring-cloud-gateway
        cloud:
          gateway:
            routes:
            - id: bamboo_route
              uri: https://blog.csdn.net/
              predicates:
      #        - Path=/blogdevteam
              - Path=/test/blogdevteam
              filters:
              - StripPrefix=1 #去掉前缀
              - AddResponseHeader=X-Response-Default-Foo, Default-Bar #返回消息头添加head信息
      

      StripPrefix=1 #去掉第1个前缀以/分割

      AddResponseHeader返回报文消息头添加head信息

      这里只列举几个重要的过滤器

      Hystrix GatewayFilter工厂

      Hystrix是Netflix的一个库,用于实现断路器模式。Hystrix GatewayFilter允许您将断路器引入网关路由,保护您的服务免受级联故障的影响,并允许您在下游故障的情况下提供后备响应。

      要在项目中启用Hystrix GatewayFilters,请spring-cloud-starter-netflix-hystrix从Spring Cloud Netflix添加依赖项。

      Hystrix GatewayFilter工厂需要一个name参数,它是的名称HypythonstrixCommand。

      spring:
        cloud:
          gateway:
            routes:
            - id: hystrix_route
              uri: https://example.org
              filters:
              - Hystrix=myCommandName
      

      这会将其余的过滤器包装在HystrixCommand带有命令名的中myCommandName。

      Hystrix过滤器还可以接受可选fallbackUri参数。当前,仅forward:支持计划的URI。如果调用了后备,则请求将被转发到与URI相匹配的控制器。

      限流RequestRateLimiter GatewayFilter工厂

      RequestRateLimiter GatewayFilter Factory使用一种RateLimiter实现来确定是否允许继续当前请求。如果不是,HTTP 429 - Too Many Requests则返回状态(默认)。

      此过滤器采用一个可选keyResolver参数和特定于速率限制器的参数(请参见下文)。

      keyResolver是实现KeyResolver接口的bean 。在配置中,使用SpEL按名称引用bean。#{@myKeyResolver}是SpEL表达式,它引用名称为的bean myKeyResolver。

      Redis RateLimiter

      redis实现基于Stripe所做的工作。它需要使用spring-boot-starter-data-redis-reactiveSpring Boot启动器。

      使用的算法是令牌桶算法。

      该redis-rate-limiter.replenishRate是多么的每秒许多请求你希望用户被允许做,没有任何下降的请求。这是令牌桶被填充的速率。

      redis-rate-limiter.burstCapacity是允许用户在一个单一的第二做请求的最大数目。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求。

      通过在replenishRate和中设置相同的值,可以达到稳定的速率burstCapacity。设置burstCapacity大于可以允许临时爆发replenishRate。在这种情况下,速率限制器需要在突发之间间隔一段时间(根据replenishRate),因为2个连续的突发将导致请求丢失(HTTP 429 - Too Many Requests)。

      application.yml

      spring:
        cloud:
          gateway:
            routes:
            - id: requestratelimiter_route
              uri: https://example.org
              filters:
              - name: RequestRateLimiter
                args:
                  redis-rate-limiter.replenishRate: 10
                  redis-rate-limiter.burstCapacity: 20
      

      配置文件

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

      这定义了每个用户10的请求速率限制。允许20个并发,但是下一秒只有10个请求可用。这KeyResolver是一个简单的获取user请求参数的参数(注意:不建议在生产中使用)。

      速率限制器也可以定义为实现RateLimiter接口的Bean 。在配置中,使用SpEL按名称引用bean。#{@myRateLimiter}是SpEL表达式,它引用名称为的bean myRateLimiter。

      application.yml

      spring:
        cloud:
          gateway:
            routes:
            - id: requestratelimiter_route
        www.devze.com      uri: https://example.org
              filters:
              - name: RequestRateLimiter
                args:
                  rate-limiter: "#{@myRateLimiter}"
                  key-resolver: "#{@userKeyResolverWkvbF}"
      

      参考文档

      官方参考文档:

      https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.0.RC1/reference/html/

      总结

      以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

      0

      上一篇:

      下一篇:

      精彩评论

      暂无评论...
      验证码 换一张
      取 消

      最新开发

      开发排行榜