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。
当前路由过滤器
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
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,依次递加。