Spring cloud Gateway网关简介及使用
1、API网关
介绍:
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制
和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在
API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
作用:
2、Gateway
官方介绍:https://spring.io/projects/spring-cloud-gateway
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.7.RELEASE/reference/html/
gateway特性:
1、基于SpringFramework5,ProjectReactor和SpringBoot2.0进行构建
2、能够匹配任何任何请求属性
3、可以对路由指定Predicates和Filters
4、集成断路器
5、集成Spring Cloud服务发现
6、易于编写的Predicates和Filters
7、支持请求限流
8、支持路径重写
工作方式:
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到
网关Web处理程序。该处理程序通过特定于请求的过滤器链来运行请求。筛选器由虚线分隔的原因是,
筛选器可以在发送代理请求之前和之后都运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请 求。发出代理请求后,将运行“后”过滤器逻辑。
三个概念:
路由:路由是构建网关的基本模块,它由ID,目标URI,一系列的断言Predicates和过滤器Filters组成,
如果断言为true,则匹配该路由。
断言:参考Java8的java.util.function.Predicate,开发人员可以匹配HTTP请求中的所有内容,例如请求
头或请求参数,如果请求与断言相匹配则进行路由。
过滤:Spring框架中GatewayFilter的实例,使用过滤器,可以载请求被路由前或者后对请求进行修改。
路由断言工厂
Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping 基础架构的一部分进行匹
配。Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些断言都与HTTP请求的不同属性匹
配。您可以将多个路由断言工厂与逻辑 and 语句结合使用。
路由断言工厂RoutePredicateFactory包含的主要实现类如图所示,包含Datetime、Cookie、
Header、Host、Method、Path、Query、RemoteAddr、Weight等类型的路由断言。
Gateway过滤器工厂介绍
过滤器 有 20 多个 实现 类, 包括 头部 过滤器、 路径 类 过滤器、 Hystrix 过滤器 和 变更 请求 URL 的 过滤器,
还有 参数 和 状态 码 等 其他 类型 的 过滤器。 内置的过滤器工厂有22个实现类,包括 头部过滤器、路径过滤器、Hystrix 过滤器
、请求URL 变更过滤 器,还有参数和状态码等其他类型的过滤器。根据过滤器工厂的用途来划分,可以分为以下几种:
Header、Parameter、Path、Body、Status、Session、Redirect、Retry、RateLimiter和Hystrix
gateway全局过滤器 GlobalFilter
全局过滤器不需要在配置文件中配置,作用在所有的路由上。我们可以用它来实现很多统一化处理的业
务需求,比如负载均衡,统一过滤,路径转发,监控,日志等等。
全局过滤器加上网关过滤器组成过滤器链,该过滤器链的执行顺序是根据@Order注解指定的数字大 小,从小到大进行排序,数字越小,优先级越高。
自定义过滤器
Spring Cloud Gateway提供了过滤器的扩展功能,开发者可以根据实际业务需求来自定义
GatewayFilter网关过滤器或者GlobalFilter全局过滤器。
令牌桶算法
RequestRateLimiter底层实现是令牌桶算法; 令牌桶内存储令牌,令牌桶需要设置令牌容量,也就是系统最大的并发大;
以一定的速率生成令牌(具体速率根据系统性能设置),放到令牌桶,如果桶慢了,则丢弃;
客户端来一个请求,则先去令牌桶获取令牌,拿到令牌,则处理请求,否则 丢弃或者返回失败;
令牌桶算法的优点:
通过恒定的速率生成令牌桶,能够让请求处理更均匀,不会出现短时间大量的请求处理; 比较友好的控制高并发;
gateway快速入门
1、创建一个父级maven空项目gatewaydemo并导入依赖:
<modules>
<module>product-demo</module>
<module>order-demo</module>
<module>gateway-server</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring-cloud.version>2020.0.4</spring-cloud.version>
<springboot.version>2.5.5</springboot.version>
<springcloudalibaba.version>2.2.5.RELEASE</springcloudalibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${springcloudalibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2、创建新模块子级maven空项目product-demo(商品)、order-demo(订单)并导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
product-demo/order-demo的application.yml配置文件:
server:
port: 8080
servlet:
context-path: /
spring:
application:
name: product-demo/order-demo
product-demo:实体层Product类
public class Product {
private Integer id;
private String name;
private Integer number;
private Double price;
}
public Product(Integer id, String name, Integer number, Double price) {
this.id = id;
this.name = name;
this.number = number;
this.price = price;
}
product-demo:controller层ProductController 类
@RestController
@RequestMapping("/product")
public class ProductController {
@GetMapping("/{id}")
public Product detail(@PathVariable("id")Integer id){
System.out.println(1);
return new Product(id,"xxx商品"+id,2,100.00);
}
}
order-demo:实体层order类
public class Order{
private Integer id;
private String orderNo;
private Double totalPrice;
private String userInfo;
private String productInfo;
public Order(Integer id, String orderNo, Double totalPrice, String userInfo, String productInfo) {
this.id = id;
this.orderNo = orderNo;
this.totalPrice = totalPrice;
this.userInfo = userInfo;
this.productInfo = productInfo;
}
}
order-demo:controller层OrderController 类
@RestController
@RequestMapping("/order")
public class OrderController {
@GetMapping("/{id}")
public com.bdqn.pojo.Order detail(@PathVariable("id")Integer id,String info){
System.out.println(info);
return new Order(id,"1212",21.12,"用户信息"+id,"商品信息"+id);
}
}
3、创建新模块子级maven空项目gateway-server并导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- spring boot redis 缓存引入 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
</dependencies>
配置application.yml:
server:
port: 80
servlet:
context-path: /
spring:
application:
name: gateway-server
cloud:
gateway:
routes: # 路由规则定义
- id: product-service # 路由ID
uri: http://localhost:8080/ # 路由地址
predicates: # 断言规则
- Path=/product/**
- id: order-service # 路由ID
uri: http://localhost:8081/ # 路由地址
predicates: # 断言规则
- Path=/order/**
匹配指定日期时间之后的请求 After:
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: after_route
uri: http://localhost:8080/
predicates:
- After=2021-04-20T06:06:06+08:00[Asia/Shanghai]
匹配指定日期时间之前的请求 Before
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: before_route
uri: http://localhost:8080/
predicates:
- Before=2021-12-20T06:06:06+08:00[Asia/Shanghai]
匹配指定日期时间之间的请求 Between
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: betwwen_route
uri: http://localhost:8080/
predicates:
- Between=2021-01-20T06:06:06+08:00[Asia/Shanghai],2021-04-
20T06:06:06+08:00[Asia/Shanghai]
Cookie路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8080/
predicates:
- Cookie=token, \d+
Host路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: host_route
uri: http://localhost:8080/
predicates:
- Host=**.somehost.org,**.anotherhost.org
Header路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:8080/
predicates:
- Header=X-Request-Id, \d+
Method路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: method_route
uri: http://localhost:8080/
predicates:
- Method=GET,POST
Path路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: path_route
uri: http://localhost:8080/
predicates:
- Path=/product/{segment}
Query路由匹配规则
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: query_route
uri: http://localhost:8080/
predicates:
- Query=green
Gateway过滤器
spring:
application:
name: gateway-server
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://localhost:8080/
predicates:
- Path=/product/** , /api-gateway/**
filters:
- AddRequestParameter=info, hehe #添加请求参数(属于前置过滤网关)
- RewritePath=/api-gateway(?<segment>/?.*), $\{segment} #将/api-gateway/product/请求重写成/product/请求(属于前置过滤网关)
- SetStatus=456 #无论哪种情况,响应的HTTP状态都设置为456(属于后置过滤网关)
- AddResponseHeader=X-Response-Author, java1234 #返回信息,添加header头信息(属于后置过滤网关)
4、编写代码:
gateway-server:config层:KeyResolverConfiguration
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver ipKeyResolver(){
// return exchange -> Mono.just(exchange.getRequest().getURI().getPath());//URI限流
//Mono.just(exchange.getRequest().getQueryParams().getFirst("token")); // 参数限流
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());//IP限流
}
}
filter层MyCustomerGatewayFilter 类
public class MyCustomerGatewayFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return -1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
System.out.println("自定义网关过滤器");
return chain.filter(exchange);
}
}
filter层:MyCustomerGlobalFilter 类
public class MyCustomerGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain
chain) {
System.out.println("自定义全局过滤器");
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -2;
}
}
5、运行结果:(redis可视化工具中也有记录报错)