GateWay -API网关
SpringCloud Alibaba 风格组件
文章目录
- GateWay -API网关
- 前言
- 什么是API网关
- 网关的组成
- 路由转发
- 过滤器
- 一、SpringCloud Alibaba Gateway介绍
- 1.1 简介
- 1.2 名词解释
- 1.2.1 Route
- 1.2.2 Predicate
- 1.3 工作原理图
- 二、小案例演示
- 2.1 构建一个商品销售的微服务框架
- 2.2 编写一个SpringCloud Alibaba的Gateway的yml配置文件
- 2.3 测试
- 三、常用的断言策略
- 3.1时间戳断言
- 3.2 Cookie断言
- 3.3 header路由断言
- 3.4 Host路由匹配规则
- 3.5 Method路由匹配规则
- 3.6 Path匹配路由规则
- 3.7 Weight权重匹配规则
- 四、Gateway过滤器
- 4.1 Gateway路由Filter工厂介绍
- 4.2 常用GatewayFilter实例说明
- 4.2.1 前置过滤器
- :one: AddRequestHeader GatewayFilter Factory
- :two:The RewritePath GatewayFilter Factory
- 4.2.2 后置过滤器
- :one:The SetStatus GatewayFilter Factory
- :two:AddResponseHeader GatewayFilter Factory
- 4.3 Gateway全局Filter GlobalFilter
- 4.4 自定义过滤器:fire::fire:
- 4.4.1 自定义Gateway过滤器
- 4.4.2 自定义GlobalFilter过滤器
- 五、Gateway网管限流
- 5.1 令牌桶网管限流算法
- 5.2 Gateway网关限流实例
- 5.2.1 URI限流
- 5.2.2 参数限流
- 5.2.3 ip限流
前言
什么是API网关
API网关作用就是把各个服务对外提供的API汇聚起来,让外界看起来就像是一个统一的接口。同时也可以在网关中提供额外的功能。
🔥总结: 网关就是所有项目的统一的入口
网关的组成
网关 = 路由转发+过滤器(额外编写的功能)
路由转发
接受外界的请求,通过网关的路由转发,转发到后端的服务上
如果只有一个服务功能看起来就像是之前所学习的nginx反向代理服务器一样,外界访问nginx,由nginx来做负载均衡,后请求转发到对应的服务器上。
过滤器
网关非常重要的功能就是过滤器。
过滤器中提供了默认的25内置功能还能自定义功能。
对于我们来说比较常用的功能有:网关的容错,限流以及请及相应的额外处理。
提示:以下是本篇文章正文内容,下面案例可供参考
一、SpringCloud Alibaba Gateway介绍
1.1 简介
SpringCloud Alibaba Gateway是Spring Cloud的二级子项目,提供了微服务网关功能,包含:权限安全、监控/指标等功能。
1.2 名词解释
在学习Gateway时里面有一些名词需要提前解释了解,他对于后面的学习是与很大的帮助的。
1.2.1 Route
Route中文称为路由,Gateway 里面的Route是主要学习内容,一个Gateway项目可以包含多个Route。
一个路由包含ID
、URI
、[Predicate集合]、[Filter集合]。
在Router中ID是自定义的,URI就是一个地址,剩下的Predicate和Filter学习明白了,Route就清楚了。
1.2.2 Predicate
Predicate:谓词
谓词对学习Gateway比较重要的一点!!
简单点理解谓词就是一些附加条件和内容。
2.3 Filter
所有生效的Filter都是GatewayFilter的实例。
在Gateway运行过程中Filter 负责在代理服务"之前”或“之后”去做一些事情。
1.3 工作原理图
- 客户端向SpringCloud Gateway发出请求,
然后在Gateway Handler Mapping中找到相应的请求映射地址的路由匹配,将其发送到Gateway web handler
-
Handler再通过指定的过滤器链来请求发送到我们实际的业务逻辑,然后再返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。 - Filter会根据其发出的请求类型来做出相应的响应。
二、小案例演示
2.1 构建一个商品销售的微服务框架
注意要使用Gateway需要引入其依赖文件
<dependencies>
<!--引入gateway的依赖文件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
不能引入web依赖,只需要在父工程中声明版本即可,否则会报错
2.2 编写一个SpringCloud Alibaba的Gateway的yml配置文件
server:
port: 81 # 统一访问的端口为81端口
servlet:
context-path: /
spring:
application:
name: gateway-server
# 路由配置
cloud:
gateway:
routes: # 定义路由规则 可以定义多个路由 统一的路径管理
- id: product # 路由ID 保证其唯一 一般与项目名保持一致即可
uri: http://localhost:8080/ # 路由地址 项目的地址
predicates: # 断言规则
- Path=/product/**
- id: order # 路由ID 唯一保证 与项目名保持一致即可
uri: http://localhost:8081/ # 路由地址
predicates: # 断言规则
- Path=/order/**
2.3 测试
所以,我们可以不恰当的把gateway理解成我们学校宿舍的舍管阿姨 或者保安队长,具有统一协调和分配的权力
三、常用的断言策略
SpringCloud Gateway的官网为我们提供了11中断言策略
官网地址 :https://cloud.spring.io/spring-cloud-gateway/reference/html/
- The After Route Predicate Factory
- The Before Route Predicate Factory
- The Between Route Predicate Factory
- The Cookie Route Predicate Factory
- The Header Route Predicate Factory
- The Host Route Predicate Factory
- The Method Route Predicate Factory
- The Path Route Predicate Factory
- The Query Route Predicate Factory
- The RemoteAddr Route Predicate Factory
3.1时间戳断言
The After Route Predicate Factory
可以通过 下述的方法来获得符合格式的时间戳字符串
server:
port: 81 # 统一访问的端口为81端口
servlet:
context-path: /
spring:
application:
name: gateway-server
# 路由配置
cloud:
gateway:
routes: # 定义路由规则 可以定义多个路由 统一的路径管理
- id: product # 路由ID 保证其唯一 一般与项目名保持一致即可
uri: http://localhost:8080/ # 路由地址 项目的地址
predicates: # 断言规则
- Path=/product/**
- After=2022-06-18T19:16:43.338+08:00[Asia/Shanghai] # 在这个时间戳之后的请求才可以
解释说明:
使用了After的断言策略,就相当于给Path的策略上又加了一个条件。请求只有当先符合了路径与/product/xx的条件后,在符合请求的时间在2022-06-18T19:16:43.338+08:00之后的请求才可以被真正的响应到。
After是在xx时间节点之后的请求,才可以被通过
故同理可以得出Before是之前的请求,Between是在两个时间节点之间的请求
3.2 Cookie断言
使用Cookie断言有两个参数,一个是Cookie名称;一个Java正则表达式。
这个断言匹配给定的cookie名和值正则匹配的请求。
server:
port: 81 # 统一访问的端口为81端口
servlet:
context-path: /
spring:
application:
name: gateway-server
# 路由配置
cloud:
gateway:
routes: # 定义路由规则 可以定义多个路由 统一的路径管理
- id: product # 路由ID 保证其唯一 一般与项目名保持一致即可
uri: http://localhost:8080/ # 路由地址 项目的地址
predicates: # 断言规则
- Path=/product/**
- Cookie=token, \d+
- id: order # 路由ID 唯一保证 与项目名保持一致即可
uri: http://localhost:8081/ # 路由地址
predicates: # 断言规则
- Path=/order/**
- Cookie=token,
这里的话,给Product服务添加一个键名为token,值为大于一个数字的cookie作为判断条件,使用postman测试
3.3 header路由断言
Header路由匹配规则有两个参数;一个报文头name;一个正则表达式
。该断言与具有给定名称的头部信息匹配,该标头的值与正则表达式匹配,以下为实例:
- Header= X-Request-hid,
3.4 Host路由匹配规则
该host路由断言工厂需要一个参数:主机名的列表patterns,该模式下是带有,
隔符的Ant样式的模式,断言与Host匹配模式的标头匹配,以下实例配置主机路由断言。
需要匹配的域名,多个用逗号分隔,支持Ant风格的模式匹配
- Host=**.somehost.org,**.antherhost.org
如果需要测试,我们可以修改localhost对应的主机地址的映射,在C:\WINDOWS\System32\drivers\etc目录下找到host文件,可以增加一个本机地址所对应的域名比如www.somehost,org。然后重启项目再次访问即可以实现。
3.5 Method路由匹配规则
使用Method匹配策略有一个参数,即为具体的某一个Methods,如果使用多个Methods的提交匹配方式的话,需要使用,
进行分割。
- Method=GET,POST
使用PostMan进行测试,使用get或者post的方式提交的话,都是可以访问到的。但是,使用Put或者delete访问,则是404
3.6 Path匹配路由规则
接收一个匹配路径的参数来判断是否走路由。在该模式下,有两个参数,分别是patterns, matchTrailingSlash,第一个表示匹配的模式路径,第二个参数是一个布尔类型的值,默认是true,表示支持的模式匹配类型。
使用Path模式匹配,默认是支持Spring’的模式匹配规则,如REST风格的匹配
- Path=/order/{segment} # /order路径下的参数名为Segment的格式
- Path=/order/**# 。order路径下的所有子路径都匹配,可以有参数,也可以没有。
3.7 Weight权重匹配规则
该Weight路由断言工厂有两个参数,一个是Group,一个是Weight(一个int)。权重是按照组来进行计算的,以下的实例配置权重断言路由:
spring:
# 路由配置
cloud:
gateway:
routes:
- id: product
uri: http://localhost:8080/
predicates:
- Weight=group1,8
- id: order
uri: http://localhost:8081/
predicates:
- Weight=group1,2
这条路线上的流量大约有80%会流向http://localhost:8080/ ,有20%的流量会流向http://localhost:8081/这个分支。
四、Gateway过滤器
gateway过滤器分为Pre类型的过滤器和Post类型的过滤器。
- Pre类型的过滤器:在请求转发到后端微服务之前执行,在Pre类型过滤器链中可以做鉴权,限流等操做。
- Post类型的过滤器:在请求执行完成之后,再将结果返回给客户端之前执行。
在Spring Cloud Gateway中内置了许多的过滤器,Filter分两种实现,一种是GatewayFilter
和GlobalFilter
,GlobalFilter会在全局过滤器应用到所有的路由上,而GatewayFilter智慧应应用到单个路由或者一个分组的路由上。
4.1 Gateway路由Filter工厂介绍
过滤器有20多个实现类,包括头部 过滤器 ,路径过滤器,类过滤器,Hystrix过滤器和变更过滤器,请求过滤器,URL过滤器,参数过滤器,状态码过滤器等等
4.2 常用GatewayFilter实例说明
4.2.1 前置过滤器
1️⃣ AddRequestHeader GatewayFilter Factory
用法:
spring:
application:
name: gateway-server
# 路由配置
cloud:
gateway:
routes:
- id: product
uri: http://localhost:8080/
predicates: # 断言规则
- Path=/product/**
filters:
- AddRequestParameter=info,
相当于给http://localhost:8080/product/** 地址上的服务的增加了一个参数,参数名为info,值为blue,然后我们在COntroller层打印输出一下这个参数,再次访问,即可在控制台输出blue
2️⃣The RewritePath GatewayFilter Factory
可重写的gateway过滤器工厂,
- id: order # 路由ID 唯一保证 与项目名保持一致即可
uri: http://localhost:8081/ # 路由地址
predicates:
- Path=/order/**,/api-gateway/**
filters:
- AddRequestParameter=info, blue
- RewritePath=/api-gateway(?<segment>/?.*), $\{segment}
我们如果要使用RewritePath的过滤器的话,我们可以现在断言处加一个API访问路由,在添加一个重写的路由去发访问。
我们先访问http://localhost:81/api-gateway/order/1001
在访问http://localhost:81/order/1001 发现也是可以访问的 。
因为这里直接走的是/product/** 这个访问路由,所以,我们再将/order/**这个访问路由去掉。再次访问。发现我们只能访问http://localhost:81/api-gateway/order/1001这个路由。
4.2.2 后置过滤器
1️⃣The SetStatus GatewayFilter Factory
spring:
application:
name: gateway-server
# 路由配置
cloud:
gateway:
routes:
- id: order
uri: http://localhost:8081/
predicates:
- Path=/order/**
filters:
- AddRequestParameter=info, blue
- SetStatus=201 # 默认的是200 在这里改成了201
从这里可以明显的看出,这是一个后置的过滤网关处理器。
2️⃣AddResponseHeader GatewayFilter Factory
在请求后,返回信息,添加一个Header的信息
- id: order # 路由ID 唯一保证 与项目名保持一致即可
uri: http://localhost:8081/ # 路由地址
predicates:
# - Path=/api-gateway/**
- Path=/order/**
filters:
- AddRequestParameter=info, blue
- AddRequestHeader=X-Request-Author,
4.3 Gateway全局Filter GlobalFilter
全局过滤器不需要在配置文件中配置,作用在所有的路由上,我们可以用来实现很多统一化处理的业务需求,比如负载均衡,路径转发,监控,日志等等,
全局过滤器+网管过滤器=过滤器链
,该过滤器链的执行顺序是根据 @Order注解指定的数字大小,从小到大进行排序,数字越小,优先级越高。
4.4 自定义过滤器🔥🔥
SpringCloud Gateway提供过滤器的扩展功能,开发者可以根据实际的业务需求来自定义gatewayFilter网关过滤器或者GlobalFilter全局过滤器。
4.4.1 自定义Gateway过滤器
要完成自定义的网管过滤器,就先要实现GatewayFilter,Ordered接口,以及配置类
【步骤一”】编写自定义的过滤器类MyCustomerGatewayFilter
@Component
public class MyCustomerGatewayFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
// TODO Auto-generated method stub
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// TODO Auto-generated method stub
System.out.println("自定义网关过滤器...");
Mono<WebSession> session = exchange.getSession();
System.out.println(session);
return chain.filter(exchange);
}
}
【步骤二"】编写一个自定义网关过滤器的配置类,来实现之前yml的写法。比如配置path,id,uri以及将自定义的过滤器类添加进Spring的Bean容器中统一管理。
@Configuration
public class MyCustomerGatewayFilterConfig {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r->r
.path("/product/**")
.uri("http://localhost:8080/")
.filters(new MyCustomerGatewayFilter())
.id("product")
).build();
}
}
【步骤三"】去除yml里面的配置,重新启动项目,访问http://localhost:81/product/1002,发现访问成功。
4.4.2 自定义GlobalFilter过滤器
要自定义GlobalFilter的,首先一样也是要实现Ordered类,只不过与自定义网管过滤器不同的是,全局过滤器还需要实现GlobalFilter
接口
@Component
public class MyCustomerGatewayFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
// TODO Auto-generated method stub
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// TODO Auto-generated method stub
System.out.println("自定义网关过滤器...");
return chain.filter(exchange);
}
}
发现也是没有问题的,这时候,我们观察控制台的输出:
五、Gateway网管限流
5.1 令牌桶网管限流算法
RequestRateLimiter底层实现的原理就是令牌桶算法
令牌桶内存储令牌,令牌桶需要设置令牌容量,也就是系统最大的并发量。
以一定的速率去生成令牌(具体的速率就根据系统的性能设置),放到令牌桶,如果桶满了,则丢弃;
客户端来一个请求,则先去桶里面获取令牌,拿到令牌后,则处理请求,否则直接丢弃或者返回失败。
令牌桶算法的优点:
- 通过恒定的速率生成令牌,能够让请求处理更均匀,不会出现短时间内大量的请求处理,可以很好的控制并发;
5.2 Gateway网关限流实例
Spring Cloud Gateway官方提供了RequestRateLimiterFilterFactory过滤器工厂,使用Redis和Lua脚本实现令牌桶,来实现网关限流:
添加Redis依赖:
<!--Spring Boot Redis的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--Letture Pool缓存连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
Yml文件配置
server:
port: 81
servlet:
context-path: /
spring:
application:
name: gateway-server
redis:
host: 192.168.188.129
port: 6379
password: null
jedis:
pool:
max-active: 100
max-idle: 100
min-idle: 100
max-wait: 60000
cloud:
gateway:
routes:
- id: product
uri: http://localhost:8080/
predicates:
- Path=/product/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 2
redis-rate-limiter.requestedTokens: 1
key-resolver: '#{@pathKeyResolver}'
- id: order
uri: http://localhost:8081/
predicates:
- Path=/order/**
🔥说明:
5.2.1 URI限流
编写一个配置类
@Configuration
public class KeyResolverConfig {
@Bean
public KeyResolver pathKeyResolver() {
/*
* return new KeyResolver() {
*
* @Override public Mono<String> resolve(ServerWebExchange exchange) { // TODO
* Auto-generated method stub return
* Mono.just(exchange.getRequest().getURI().getPath()); } };
*/
return exchange -> Mono.just(exchange.getRequest().getURI().getPath()); // URI限流
}
}
5.2.2 参数限流
@Configuration
public class KeyResolverConfig {
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getQueryParams.getFirst("token")); // 参数限流
}
}
5.2.3 ip限流
/**
* 根据主机名限流
* @return
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); // IP限流
}
然后我们访问多次http://localhost:81/product/1001地址,出现如下图的情况,说明已经实现了流量控制,我们查看redis客户端的key发现 有两个多出来的key