一、概述
(1)是什么
- 官网:Spring Cloud Gateway
- 体系定位
(2)微服务架构中网关在哪里
(3)能干嘛
- 反向代理
- 鉴权
- 流量控制
- 熔断
- 日志监控
(4)总结
二、Gateway 三大核心
(1)总述官网
(2)分
2.2.1Route(路由)
路由是构建网关的基本模块,它由 ID,目标 URI,一系列的断言和过滤器组成,如果断言为 true 则匹配该路由
2.2.2Predicate(断言)
- 参考的是 Java8 的 java.util.function.Predicate
- 开发人员可以匹配 HTTP 请求中的所有内容 (例如请求头或请求参数),如果请求与断言相匹配则进行路由
2.2.3Filter(过滤)
指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
(3)总结
- web前端请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制
- predicate就是我们的匹配条件
- filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了
三、Gateway 工作流程
(1)官网总结
- 客户端请求发送到 Spring Cloud Gateway 后,先通过 Gateway Handler Mapping 匹配路由,再交给 Gateway Web Handler。Handler 借助过滤器链,将请求发往实际服务处理业务并返回结果
- 过滤器分两类:
- Pre 过滤器:在代理请求前执行,用于参数校验、权限校验、流量监控、日志输出、协议转换等
- Post 过滤器:在代理请求后执行,可修改响应内容 / 头,输出日志、监控流量等,作用关键
(2)核心逻辑
路由转发+断言判断+执行过滤器链
四、入门配置
(1)建Module
cloud-gateway9527
(2)改POM
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.atguigu.cloud</groupId>
<artifactId>mscloudV5</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>cloud-gateway9527</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
(3)写YML
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true
service-name: ${spring.application.name}
(4)主启动
(5)业务类
无,不写任何业务代码,网关和业务无关
(6)测试
- 先启动 8500 服务中心 Consul
- 再启动 9527 网关入驻
五、9527 网关如何做路由映射
(1)9527 网关如何做路由映射那???
5.1.1诉求
我们目前不想暴露8001端口,希望在8001真正的支付微服务外面套一层9527网关
5.1.28001 新建 PayGateWayController
5.1.3启动 8001 支付
5.1.48001 自测通过
- http://localhost:8001/pay/gateway/get/1
- http://localhost:8001/pay/gateway/info
(2)9527 网关 YML 新增配置
(3)测试 1
5.3.1启动 Consul8500 服务
5.3.2启动 8001 支付
5.3.3启动 9527 网关
5.3.4访问说明
(1)添加网关前
(2)隐真示假,映射说明
(3)添加网关后
5.3.5目前 8001 支付微服务前面添加 GateWay 成功
(4)测试 2
5.4.1启动订单微服务测试,看看是否通过网关?
(1)修改 cloud-api-commons
PayFeignApi接口
(2)修改 cloud-consumer-feign-order80
(3)网关开启
(4)网关关闭
(5)结论
- 9527 网关是否启动,毫无影响,o (π﹏π) o
- 目前的配置来看,网关被绕开了……
5.4.2正确做法
(1)同一家公司自己人,系统内环境,直接找微服务
(2)不同家公司有外人,系统外访问,先找网关再服务
- 刷新 feign 接口 jar 包
- 重启 80 订单微服务
- 有网关正常 success
- 无网关异常
(5)还有问题
请看看网关 9527 的 yml 配置,映射写死问题,^_^
六、GateWay 高级特性

(1)Route 以微服务名 - 动态获取服务 URI
6.1.1痛点
6.1.2是什么
6.1.3解决 uri 地址写死问题
- 9527 修改前 YML
- 9527 修改后 YML
6.1.4测试 1
- 重启网关 9527,80/8001 保持不变
- http://localhost/feign/pay/gateway/get/1
6.1.5测试 2
- 如果将 8001 微服务 yml 文件端口修改为 8007,照样访问
- 我实际启动的程序是 main8001 但是端口名改为 8007
- 我们依据微服务名字,匹配查找即可uri: lb://cloud-payment-service
(2)Predicate 断言 (谓词)
6.2.1是什么
- Spring Cloud Gateway
- Route Predicate Factories这个是什么东东?
6.2.2启动微服务 gateway9527,看看 IDEA 后台的输出
6.2.3整体架构概述
6.2.4常用的内置 Route Predicate
(1)配置语法总体概述
- 两种配置,二选一(Most examples below use the shortcut way)
Configuring Route Predicate Factories and Gateway Filter Factories :: Spring Cloud Gateway
- Shortcut Configuration
- Fully Expanded Arguments
(2)测试地址
http://localhost:9527/pay/gateway/get/1
(3)常用断言 api
- After Route Predicate
package com.atguigu.test; import java.time.ZonedDateTime; public class ZonedDateTimeDemo { public static void main(String[] args) { ZonedDateTime zbj = ZonedDateTime.now(); System.out.println(zbj); } }
- Before Route Predicate
- Between Route Predicate
- Cookie Route Predicate
- 方法1,原生命令
- 方法2,postman
- 方法3,chrome浏览器
- 方法1,原生命令
- Header Route Predicate
- 方法1,原生命令
- 方法2,postman
- 方法1,原生命令
- Host Route Predicate
- 方法1,原生命令
- 方法2,postman
- 方法1,原生命令
- Path Route Predicate
- Query Route Predicate
- RemoteAddr route predicate
- Method Route Predicate
配置某个请求地址,只能用 Get/Post 方法访问,方法限制
(4)上述配置小总结
6.2.5自定义断言,XXXRoutePredicateFactory 规则
(1)痛点
- 原有的断言配置不满足业务怎么办?
- 看看 AfterRoutePredicateFactory
- 架构概述
- 模板套路
- 要么继承 AbstractRoutePredicateFactory 抽象类
- 要么实现 RoutePredicateFactory 接口
- 开头任意取名,但是必须以 RoutePredicateFactory 后缀结尾
(2)自定义路由断言规则步骤套路
- 编写步骤
- 新建类名 XXX 需要以 RoutePredicateFactory 结尾并继承 AbstractRoutePredicateFactory 类
- 重写 apply 方法
- 新建 apply 方法所需要的静态内部类 MyRoutePredicateFactory.Config
(补充说明:这个 Config 类就是我们的路由断言规则,重要)注意:要在pom文件里添加依赖<!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> <scope>provided</scope> </dependency>
- 空参构造方法,内部调用 super
- 重写 apply 方法第二版
- 新建类名 XXX 需要以 RoutePredicateFactory 结尾并继承 AbstractRoutePredicateFactory 类
- 完整代码 V1
package com.atguigu.cloud.mygateway; import jakarta.validation.constraints.NotEmpty; import lombok.Getter; import lombok.Setter; import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; import java.util.function.Predicate; @Component public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> { //这个Config类就是我们的路由断言规则,重要!!! @Validated public static class Config { @Setter @Getter @NotEmpty private String userType; //钻/金/银和yml配置的会员等级 } @Override public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config) { return new Predicate<ServerWebExchange>() { @Override public boolean test(ServerWebExchange serverWebExchange) { //检查request的参数里面,userType是否为指定的值,符合配置就通过 String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType"); if (userType == null) { return false; } //如果说参数存在,就和config的数据进行比较 if(userType.equalsIgnoreCase(config.getUserType())){ return true; } return false; } }; } public MyRoutePredicateFactory() { super(MyRoutePredicateFactory.Config.class); } }
(3)测试 1
- YML
- 启动后???
- 故障现象
- 导致原因
- 解决方案
- 先解决问题,让我们自定义的能用
- Fully Expanded Arguments
- http://localhost:9527/pay/gateway/get/1?userType=diamond
- success
- 故障现象
(4)bug 分析
缺少 shortcutFieldOrder 方法的实现,所以不支持短格式
(5)测试 2
- 完整代码 02(再加上下面这段)
- YML
- 重启 9527 并测试
(3) Filter过滤
6.3.1概述
(1)官网
(2)一句话
- SpringMVC 里面的拦截器 Interceptor,Servlet 的过滤器
- “pre” 和 “post” 分别会在请求被执行前调用和被执行后调用,用来修改请求和响应信息
能干嘛
(3)能干嘛
- 请求鉴权
- 异常处理
- 记录接口调用时长统计,重点,大厂面试设计题
(4)类型
- 全局默认过滤器 Global Filters
- Spring Cloud Gateway
- gateway 出厂默认已有的,直接用即可,主要作用于所有的路由
- 不需要在配置文件中配置,作用在所有的路由上,实现 GlobalFilter 接口即可
- 单一内置过滤器 GatewayFilter
- Spring Cloud Gateway
- 也可称为网关过滤器,主要作用于单一路由或某个路由分组
- 自定义过滤器
6.3.2Gateway内置的过滤器
(1)是什么
(2)只讲解常见和通用的,Not All
(3)常用的内置过滤器
- 请求头 (RequestHeader) 相关组
- The AddRequestHeader GatewayFilter Factory
- 指定请求头内容 ByName
- 8001 微服务 PayGateWayController 新增方法
- 9527 网关 YML 添加过滤内容
- 重启 9527 和 8001 并再次调用地址
- The RemoveRequestHeader GatewayFilter Factory
- 删除请求头 ByName
- 修改前
- YML
- 重启 9527 和 8001 并再次调用地址
- 修改后
- The SetRequestHeader GatewayFilter Factory
- 修改请求头 ByName
- 修改前 (sec-fetch-mode)
- YML
- 重启 9527 和 8001 并再次调用地址
- 修改后
- The AddRequestHeader GatewayFilter Factory
- 请求参数 (RequestParameter) 相关组
- The AddRequestParameter GatewayFilter Factory
- The RemoveRequestParameter GatewayFilter Factory
- 上述两个合一块
- YML
- 修改 PayGateWayController
- 测试
- YML
- 回应头 (ResponseHeader) 相关组
- 开启配置前,按照地址 chrome 查看一下
- The AddResponseHeader GatewayFilter Factory
- The SetResponseHeader GatewayFilter Factory
- The RemoveResponseHeader GatewayFilter Factory
- 开启配置后,上面三个配置打包一块上(见上)
- 开启配置前,按照地址 chrome 查看一下
- 前缀和路径相关组
- The PrefixPath GatewayFilter Factory
- 自动添加路径前缀
- 之前的正确地址:http://localhost:9527/pay/gateway/filter
- YML
- Chrome 测试
- The SetPath GatewayFilter Factory
- 访问路径修改
- 测试
- The RedirectTo GatewayFilter Factory
- 重定向到某个页面
- YML
- The PrefixPath GatewayFilter Factory
- 其它
- Default Filters
- 配置在此处相当于全局通用,自定义秒变 Global
6.3.3Gateway自定义过滤器
(1)自定义全局 Filter
- 面试题
- 统计接口调用耗时情况,如何落地,谈谈设计思路
- 通过自定义全局过滤器搞定上述需求
- 案例
- 自定义接口调用耗时统计的全局过滤器
- 知识出处:Spring Cloud Gateway
- 步骤
- 新建类 MyGlobalFilter 并实现 GlobalFilter,Ordered 两个接口
- YML(注意,把- My的那个断言去掉,我这里忘记去掉了,留着它会影响访问)
- code
- 新建类 MyGlobalFilter 并实现 GlobalFilter,Ordered 两个接口
- 测试
(2)自定义条件 Filter
- 自定义,单一内置过滤器 GatewayFilter
- 先参考 GateWay 内置出厂默认的
- SetStatusGatewayFilterFactory
- SetPathGatewayFilterFactory
- AddResponseHeaderGatewayFilterFactory
- ......
- 自定义网关过滤器规则步骤套路
- 新建类名 XXX 需要以 GatewayFilterFactory 结尾
- 并继承 AbstractGatewayFilterFactory 类
- 新建 XXXGatewayFilterFactory.Config 内部类
- 重写 apply 方法
- 重写 shortcutFieldOrder
- 空参构造方法,内部调用 super
- 完整代码
package com.atguigu.cloud.mygateway; import lombok.Getter; import lombok.Setter; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.ArrayList; import java.util.List; @Component //千万不能忘记!!! public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config> { public static class Config{ @Getter @Setter private String status; } @Override public GatewayFilter apply(MyGatewayFilterFactory.Config config) { return new GatewayFilter(){ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); System.out.println("进入自定义网关过滤器MyGatewayFilterFactory,status====" + config.status); if (request.getQueryParams().containsKey("atguigu")) { return chain.filter(exchange); }else { exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST); return exchange.getResponse().setComplete(); } } }; } @Override public List<String> shortcutFieldOrder() { List<String> list = new ArrayList<String>(); list.add("status"); return list; } public MyGatewayFilterFactory() { super(MyGatewayFilterFactory.Config.class); } }
- YML
- 测试
七、Gateway 整合阿里巴巴 Sentinel 实现容错
见后续springcloud alibaba篇章