SpringCloud Gateway基础入门

181 阅读9分钟

Gateway网关

  • 什么是服务网关

API Gateway(APIGW / API 网关),顾名思义,是系统对外的唯一入口。API网关封装了系统内部架构,为每个客户端提供定制的API。 近几年来移动应用与企业间互联需求的兴起。从以前单一的Web应用,扩展到多种使用场景,且每种使用场景对后台服务的要求都不尽相同。 这不仅增加了后台服务的响应量,还增加了后台服务的复杂性。随着微服务架构概念的提出,API网关成为了微服务架构的一个标配组件。

  • 为什么要使用网关

微服务的应用可能部署在不同机房,不同地区,不同域名下。此时客户端(浏览器/手机/软件工具)想 要请求对应的服务,都需要知道机器的具体 IP 或者域名 URL,当微服务实例众多时,这是非常难以记忆的,对 于客户端来说也太复杂难以维护。此时就有了网关,客户端相关的请求直接发送到网关,由网关根据请求标识 解析判断出具体的微服务地址,再把请求转发到微服务实例。这其中的记忆功能就全部交由网关来操作了。

  • 核心概念

路由(Route):路由是网关最基础的部分,路由信息由 ID、目标 URI、一组断言和一组过滤器组成。如果断言 路由为真,则说明请求的 URI 和配置匹配。 断言(Predicate):Java8 中的断言函数。Spring Cloud Gateway 中的断言函数输入类型是 Spring 5.0 框架中 的 ServerWebExchange。Spring Cloud Gateway 中的断言函数允许开发者去定义匹配来自于 Http Request 中的任 何信息,比如请求头和参数等。 过滤器(Filter):一个标准的 Spring Web Filter。Spring Cloud Gateway 中的 Filter 分为两种类型,分别是 Gateway Filter 和 Global Filter。过滤器将会对请求和响应进行处理。

2.1路由谓词

Spring Cloud Gateway创建Route对象时, 使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给Route

  • Spring Cloud Gateway包含许多内置的Route Predicate Factories
  • 所有这些断言都匹配 HTTP 请求的不同属性。
  • 多个Route Predicate Factories可以通过逻辑与(and)结合起来一起使用。

路由断言工厂RoutePredicateFactory包含的主要实现类如图所示,包括Datetime、请求的远端地址、路由权重、请求头、Host 地址、请求方法、请求路径和请求参数等类型的路由断言。

Datetime

匹配日期时间之后发生的请求

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - After=2021-02-23T14:20:00.000+08:00[Asia/Shanghai]

Cookie

匹配指定名称且其值与正则表达式匹配的cookie

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Cookie=loginname, ruoyi

Header

匹配具有指定名称的请求头,\d+值匹配正则表达式

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Header=X-Request-Id, \d+

Host

匹配主机名的列表

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Host=**.somehost.org,**.anotherhost.org

Method

匹配请求methods的参数,它是一个或多个参数

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Method=GET,POST

Path

匹配请求路径

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Path=/system/**

Query

匹配查询参数

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - Query=username, abc.
 #参数名必须有username,参数值需要匹配abc,abc正则表达式
 #localhost:8080/login?username=abcde 匹配     

RemoteAddr

匹配IP地址和子网掩码

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system
           uri: http://localhost:9201/
           predicates:
             - RemoteAddr=192.168.10.1/0

Weight

匹配权重

 spring: 
   application:
     name: ruoyi-gateway
   cloud:
     gateway:
       routes:
         - id: ruoyi-system-a
           uri: http://localhost:9201/
           predicates:
             - Weight=group1, 8
         - id: ruoyi-system-b
           uri: http://localhost:9201/
           predicates:
             - Weight=group1, 2

2.2路由配置

spring cloud gateway中配置uri有三种方式,包括

  • websocket配置方式
 spring:
   cloud:
     gateway:
       routes:
         - id: ruoyi-api
           uri: ws://localhost:9090/
           predicates:
             - Path=/api/**
  • http地址配置方式
 spring:
   cloud:
     gateway:
       routes:
         - id: ruoyi-api
           uri: http://localhost:9090/
           predicates:
             - Path=/api/**
  • 注册中心配置方式
 spring:
   cloud:
     gateway:
       routes:
         - id: ruoyi-api
           uri: lb://ruoyi-api #从注册中心获取微服务地址 lb:loadbanlce
           predicates:
             - Path=/api/**

The DiscoveryClient Route Definition Locator

约定大于配置

 spring:
   cloud:
     gateway:
       discovery:
         locator:
             enable: true #是否与服务注册服务组件相结合,通过具体的serviceId转发到具体的实例
             lower-case-service-id: true #是否将服务名称转成小写
  #访问链接:localhost:8080/ruoyi-api/api/            

2.3过滤器

Spring Cloud Gateway 根据作用范围划分为 GatewayFilter 和 GlobalFilter ,二者区别如下:

GatewayFilter:网关过滤器可以分为当前路由过滤器和默认路由过滤器,当前路由过滤器配置在spring.cloud.routes.filters下,默认路由过滤器配置在spring.cloud.default-filters 下,作用在所有路由上。

GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过 GatewayFilterAdapter 包装成GatewayFilterChain 可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。

2.3.1网关过滤器(GatewayFilter)

网关过滤器用于拦截并链式处理Web请求,可以实现横切与应用无关的需求,比如:安全、访问超时的设置等。修改传入的HTTP 请求或传出HTTP响应。Spring Cloud Gateway包含许多内置的网关过滤器工厂一共有22个,包括头部过滤器、路径类过滤器、Hystrix过滤器和重写请求URL的过滤器,还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:Header、Parameter、Path、Body、Status.Session、Redirect、Retry、RateLimiter和Hystrix。

image-20220101174938521.png

当前路由过滤器

RewritePathGatewayFilterFactory

RewritePath 网关过滤器工厂采用路径正则表达式参数和替换参数,使用Java正则表达式来灵活地重写请求路径。

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/product/**,/api-gateway/**
                     filters:
                         #将 /api-gateway/product/1 重写为/product/1
                         - RewritePath=/api-gateway(?<segment>/?.*), ${segment}

PrefixPathGatewayFilterFactory

PrefixPath 网关过滤器工厂为匹配的URI添加指定前缀

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/**
                     filters:
                         #将/1 重写为/product/1
                         - PrefixPath=/product

StripPrefixGatewayFiterFactory

StripPrefix 网关过滤器工厂采用一个参数StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数。

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/**
                     filters:
                         #将/api/123/product/1 重写为/product/1
                         - StripPrefix=2

SetPathGatewayFilterFactory

SetPath 网关过滤器工厂采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了Spring Framework中的uri模板,允许多个匹配段。

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/api/product/{segment}
                     filters:
                         #将/api/123/product/1 重写为/product/1
                         - SetPath=/product/{segment}

Parameter参数过滤器

AddRequestParameter 网关过滤器工厂会将指定参数添加至匹配到的下游请求中。

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/api-gateway/**
                     filters:
                         #将 /api-gateway/product/1 重写为/product/1
                         - RewritePath=/api-gateway(?<segment>/?.*), ${segment}
                         #在下游请求中添加flag = 1
                         - AddRequestParameter=flag, 1

Status 状态过滤器

SetStatus网关过滤器工厂采用单个状态参数,它必须是有效的Spring HttpStatus。它可以是整数404或枚举NOT_FOUND的字符串表示。

 spring:
     application:
         name: gateway-server
     cloud:
         gateway:
             routes:
                 - id: product-service
                     uri: lb://product-service
                     predicates:
                         - Path=/api-gateway/**
                     filters:
                         #将 /api-gateway/product/1 重写为/product/1
                         - RewritePath=/api-gateway(?<segment>/?.*), ${segment}
                         #任何情况下将响应结果改为404
                         - SetStatus=404 

限流过滤器RequestRateLimiter

RequestRateLimiter使用一个RateLimiter接口的实现类来决定当前请求是否允许通过,如果不允许,返回HTTP 429 - Too Many Requests,这个过滤器接受一个可选的keyResolver参数和特定于速率限制器的参数(在本节后面描述)。keyResolver参数是KeyResolver接口的实现类(需要我们自定义实现),在yml配置文件中我们可以使用SpEL表达式引用自定义KeyResolver接口实现类,使用方法:{@myKeyResolver},其中myKeyResolver是Bean名

实战:

spring cloud gateway 默认使用redis的RateLimter限流算法来实现。所以我们要使用首先需要引入redis的依赖

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

定义KeyResolver

在GatewayApplicatioin引导类中添加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver来指定限流的Key。

 //根据ip地址限流
 @Primary
 @Bean
 public KeyResolver ipKeyResolver(){
     return new KeyResolver() {
         @Override
         public Mono<String> resolve(ServerWebExchange exchange) {
             return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
         }
     };
 }
 ​
 //根据请求路径限流
 @Bean
 public KeyResolver pathKeyResolver(){
     return new KeyResolver() {
         @Override
         public Mono<String> resolve(ServerWebExchange exchange) {
             return Mono.just(exchange.getRequest().getPath().value());
         }
     };
 }

配置文件配置redis及限流策略。某个服务的filters下,加入如下代码

 spring:
   application:
     name: sysgateway
   cloud:
     gateway:
       globalcors:
         cors-configurations:
           '[/**]': # 匹配所有请求
             allowedOrigins: "*" #跨域处理 允许所有的域
             allowedMethods: # 支持的方法
             - GET
             - POST
             - PUT
             - DELETE
       routes:
       - id: goods
         uri: lb://goods
         predicates:
         - Path=/goods/**
         filters:
         - StripPrefix= 1
         - name: RequestRateLimiter #请求数限流 名字不能随便写 
           args:
             key-resolver: "#{@ipKeyResolver}"
             redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
             redis-rate-limiter.burstCapacity: 1 #令牌桶总容量
             redis-rate-limiter.requestedTokens: 1 #每次请求从令牌桶中拿走的令牌数量
       - id: system
         uri: lb://system
         predicates:
         - Path=/system/**
         filters:
         - StripPrefix= 1
   # 配置Redis 127.0.0.1可以省略配置
   redis:
     host: 192.168.200.128
     port: 6379
 server:
   port: 9101
 eureka:
   client:
     service-url:
       defaultZone: http://127.0.0.1:6868/eureka
   instance:
     prefer-ip-address: true
  • burstCapacity:令牌桶总容量。
  • replenishRate:令牌桶每秒填充平均速率。
  • key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

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

如上配置:表示 一秒内,允许 一个请求通过,令牌桶的填充速率也是一秒钟添加一个令牌。最大突发状况 也只允许 一秒内有一次请求,可以根据业务来调整 。

默认路由过滤器

以上当前路由过滤器器全都加在spring.cloud.routes.filters下,如果加在在spring.cloud.default-filters 下就是默认路由过滤器,例如:

  routes:
     default-filters: 
         - PrefixPath=/product

2.3.2 全局过滤器GlobalFilter

image-20220101174746845.png

2.4过滤器顺序

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器。

GlobalFilter通常实现一个Ordered接口来提过order值,当前路由过滤器和默认路由过滤器由Spring指定,默认按照声明顺序从1递增。

当过滤器的order值一样时,会按照GlobalFilter>DefaultFilter>当前路由过滤器的顺序执行。

org.springframework.cloud.gateway.route.RouteDefinitionRouteLocator#getFilters()先加载defaultFilters,然后再加载某个route的filters,然后合并。

org.springframework.cLoud.gateway.handler.FilteringWebHandler#handle()方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链

每次请求都会调用FilteringWebHandler#handle()方法,合并并按照Order值排序形成一个过滤器链。DefaultFilter和当前路由过滤器会按照配置的先后顺序赋Order值,第一个配置值是1,依次递加。