Spring Cloud Gateway
1. 简介
- SpringCloud Gateway使用的 Webflux中的 reactor-netty响应式编程组件,底层使用了Netty通讯框架。
- SpringCloud Gateway是原zuul 1.X版的替代。
- Spring Cloud Gateway的目标提供统-的路由方式且基于 Filter链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流
1.1 作用
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控等
1.2 位置
- 挡在所有微服务的前面的位置
2. 三大核心概念
Route(路由)
- 路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
Predicate(断言)
- 参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter(过滤)
- 指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
总体
- 客户端向 Spring Cloud Gateway发出请求。然后在 Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到 GatewayWeb handler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。
- 核心:路由转发+执行过滤器链
3. 入门demo
- 建moudel: cloud-gateway-gateway9527
- 建pom
注意: 网关不需要web
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2020</artifactId>
<groupId>com.atguigu.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-gateway-gateway9527</artifactId>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<dependency>
<groupId>com.atguigu.springcloud</groupId>
<artifactId>cloud-api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<!--一般基础配置类-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 改yml
server:
port: 9527
spring:
application:
name: cloud-gateway
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
- 主启动类
@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527
{
public static void main(String[] args) {
SpringApplication.run(GateWayMain9527.class, args);
}
}
- 路由映射
我们需要建立一个服务提供的服务cloud-provider-payment8001,该模块的controller有get(),lb()两个方法。如果我们目前不想暴露8001端口,希望在8001外面套一层9527。 - 在gateway的yml中新增网关配置
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
#- After=2020-02-21T15:51:37.485+08:00[Asia/Shanghai]
#- Cookie=username,zzyy
#- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
eureka:
instance:
hostname: cloud-gateway-service
client: #服务提供者provider注册进eureka服务列表内
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
- 测试
-
启动7001(注册中心)
-
启动8001 cloud-provider-payment8001
-
启动9527网关
-
访问说明
-
添加网关前http://localhost:8001/payment/get/1
-
添加网关后http://localhost:9527/payment/get/1
-
-
4. 路由
Gateway网关路由有两种配置方式:
-
在配置文件yml中配置(上述)
-
代码中注入RouteLocator的Bean
4.1 代码中注入RouteLocator的Bean
- 建立配置类
GateWayConfig.class
@Configuration
public class GateWayConfig
{
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder)
{
RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
routes.route("path_route_num1",
r -> r.path("/guonei")
.uri("http://news.baidu.com")).build();
return routes.build();
}
}
- 测试
访问:http://localhost:9527/guonei
4.2 动态路由
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
-改yml
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/get/** # 断言,路径相匹配的进行路由
- id: payment_routh2 #payment_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service #匹配后提供服务的路由地址
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
- 测试
http://localhost:9527/payment/lb
8001/8002两个端口切换
5. Predicate
详细使用
输入类型是一个ServerWebExchange.我们可以使用它来匹配来自HTTP请求的任何内容,例如headers参数,After等等。
5.1 常用的Route Predicate
- 1.After Route Predicate
改yml
predicates:
- Path=/payment/lb/** # 断言,路径相匹配的进行路由
- After=2021-12-06T15:51:37.485+08:00[Asia/Shanghai]
表示在这个时间点之后才能访问lb
- 2.Before Route Predicate
- Before=2021-12-06T15:51:37.485+08:00[Asia/Shanghai] # 在这个时间点之前才能访问
- 3.Between Route Predicate
- Between=2020-10-15T20:47:39.380+08:00[Asia/Shanghai],2021-12-06T15:51:37.485+08:00[Asia/Shanghai] # 在这个时间段之间才能访问
- 4.Cookie Route Predicate
Cookie route predicate需要两个参数,一个是 Cookie name,个是正则表达式。
- Cookie=username,xyn
路由规则会通过获取对应的 Cookie name值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行
测试: 不带Cookie
测试:带Cookie
- 5.Header Route Predicate
两个参数:一个是属性名和一个正则表达式,这个属性值和正则表达式匹配则执行
- Header=X-Request-Id, \d+ # 请求头要有X-Request-Id属性并且值为整数的正则表达式
请求: curl http://localhost:9527/payment/lb -H “X-Request-Id:123”
- 6.Host Route Predicate
Host Route Predicate接收一组参数,一组匹配的域名列表,这个模板是一个ant分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。
-Host=**.yinan.com
请求: curl http://localhost:9527/payment/lb -H “Host:news.atguigu.com”
- 7.Method Route Predicate
-Method=Get #发Get请求才给访问,Post不给访问
- 8.Path Route Predicate
- 9.Query Route Predicate
带查询条件
-Query=username,\d+ #要有参数username并且值还要是整数才能路由
6 Filter(过滤)
-
Spring-Cloud-Gateway 基于过滤器实现,同 zuul 类似,有pre和post两种方式的 filter,分别处理前置逻辑和后置逻辑。客户端的请求先经过pre类型的 filter,然后将请求转发到具体的业务服务,收到业务服务的响应之后,再经过post类型的 filter 处理,最后返回响应到客户端。
-
过滤器执行流程如下,order 越大,优先级越低
-
分为全局过滤器和局部过滤器
- 全局过滤器:对所有路由生效
- 局部过滤器:对指定路由生效
-
自定义过滤器(常用)
1.主要是实现两个主要接口:implemerts GlobalFilter ,Ordered
2.一般用于全局日志记录、统一网关鉴权
- 案例
定义MyLogGateWayFilter.class
@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter,Ordered
{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
{
log.info("***********come in MyLogGateWayFilter: "+ new Date());
//获得请求参数必须带uname
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
//全局过滤器,空就过滤
if(uname == null)
{
log.info("*******用户名为null,非法用户,o(╥﹏╥)o");
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder()
{
return 0;
}
}
- 测试