前言
该博客为Sentinel
学习笔记,主要目的是为了帮助后期快速复习使用
学习视频:7小快速通关SpringCloud
辅助文档:SpringCloud快速通关
源码地址:cloud-demo
一、简介
官网:https://spring.io/projects/spring-cloud-gateway
Spring Cloud Gateway
是基于 Spring
生态系统(包括 Spring 6、Spring Boot 3 和 Project Reactor)构建的 API 网关 。它旨在为 API 提供简单而有效的路由,并处理诸如安全性、监控/指标和弹性等跨领域关注点。
1.1 主要特性
- 构建于 Spring Framework 和 Spring Boot 之上 : 利用
Spring
强大的生态系统,确保与其他Spring
项目的无缝集成。 - 灵活的路由匹配: 能够基于任何请求属性(如路径、主机、方法等)定义路由规则。
- 丰富的谓词和过滤器: 为每个路由提供特定的谓词和过滤器,方便实现复杂的路由逻辑和请求/响应的修改。
- 断路器集成 : 与
Hystrix
等断路器库集成,增强系统的弹性和容错能力。 - 服务发现集成 : 与
Spring Cloud DiscoveryClient
集成,实现动态路由和负载均衡。 - 请求速率限制: 提供请求速率限制功能,保护后端服务免受流量突增的影响。
- 路径重写: 支持对请求路径进行重写,满足不同的路由需求。
1.2 主要功能
![](https://i-blog.csdnimg.cn/direct/6322fa702d664083a0936d6d16a4700c.png)
1.3 版本选择
Spring Cloud Gateway
的两个版本主要区别在于它们的编程模型和适用场景:
- Reactive Server(响应式版本) :
- 基于
WebFlux
框架,使用响应式编程模型。 - 支持完全非阻塞的
I/O
操作,可以提高吞吐量和降低延迟。 - 适用于构建高性能、高并发的微服务架构。
- 与现代的响应式编程库(如
Reactor
)兼容。
- 基于
- Server MVC(传统阻塞式版本) :
- 基于
Spring MVC
框架,使用传统的同步阻塞I/O
模型。 - 更适合与现有的
Spring MVC
应用程序集成。 - 可能在高并发场景下的性能不如响应式版本。
- 基于
推荐使用响应式 的Gateway
二、快速入门
2.1 需求
- 客户端发送
/api/order/**
转到service-order
- 客户端发送
/api/product/**
转到service-product
- 以上转发有负载均衡效果
2.2 创建模块
创建gateway
模块,引入 spring-cloud-starter-gateway
、spring-cloud-starter-alibaba-nacos-discovery
、spring-cloud-starter-loadbalancer
xml
<!-- gateway网关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 服务发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--loadbalancer负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
添加主程序GatewayMainApplication
java
@SpringBootApplication
public class GatewayMainApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayMainApplication.class, args);
}
}
添加配置文件application.yml
yml
# 服务端口
server:
port: 80
spring:
application:
name: gateway # 服务名称
cloud:
nacos:
server-addr: 127.0.0.1:8848 # nacos地址(默认)
这里可以启动微服务gateway
、service-order
、service-product
,确保在Nacos控制台可以看到
2.3 改造微服务
创建 application-route.yml
,为 service-order
、service-prduct
添加 /api基础路径
yml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates: # 路径断言规则
- Path=/api/order/** # 路径匹配
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
并且需要在application.yml
中启用 application-route.yml
才能生效
yml
spring:
profiles:
include: route # 启用route
2.4 测试
在浏览器中输入http://localhost/api/order/readDb
,通过网关访问 service-order
微服务的接口
2.5 基础原理
- 请求处理 :
- 用户请求首先到达
Gateway
。
- 用户请求首先到达
- 路由规则(Route) :
- 网关根据预定义的路由规则(Route)来决定请求的转发路径。
- 每个路由规则包含三个主要部分:
- Predicate 断言:用于匹配请求的条件,如路径、头信息、查询参数等。
- URI 目的地:匹配请求后,请求将被转发到的目标地址。
- Filter 过滤器:在请求转发前后执行的一系列过滤器链,用于修改请求/响应、进行认证等。
- 过滤器链(Filter Chain) :
- 请求在转发到目的地之前,会先经过一系列过滤器(Filter)的处理。
- 过滤器可以执行各种任务,如日志记录、请求修改、认证授权等。
- 过滤器按照定义的顺序执行,形成一个过滤器链。
- 转发请求 :
- 经过过滤器链处理后,请求被转发到最终的目的地(服务提供者)。
- 响应处理 :
- 目的地处理完请求后,将响应返回给网关。
- 响应同样会经过过滤器链的处理,然后返回给用户。
![](https://i-blog.csdnimg.cn/direct/97805295414e4670985ce1dd5e200dfb.png)
三、Predicate - 断言
3.1 断言写法
3.1.1 短写法
快捷方式配置由过滤器名称识别,后跟等号 (=
),后跟用逗号 (,
) 分隔的参数值。
yml
# 断言短写法
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates: # 路径断言规则
- Path=/api/order/** # 路径匹配
3.1.2 全写法
完全展开的参数看起来更像是带有名称/值对的标准 yaml
配置。通常,将有一个 name
键和一个 args
键。args
键是用于配置谓词或筛选条件的键值对的映射。
yml
# 断言全写法
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates:
- name: Path # 路径断言
args: # 参数
pattern: /api/order/** # 匹配路径
matchTrailingSlash: true # 匹配尾部斜杠
3.2 断言工厂
3.3 自定义断言工厂
创建XxxRoutePredicateFactory
继承AbstractRoutePredicateFactory
类
java
/**
* Vip断言工厂
*/
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {
public VipRoutePredicateFactory() {
super(Config.class);
}
// 配置参数的顺序,为短写法准备
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("param", "value");
}
// 断言逻辑
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange serverWebExchange) {
// localhost/search?q=haha&user=jyh
String first = serverWebExchange.getRequest().getQueryParams().getFirst(config.param);
return StringUtils.hasText(first) && first.equals(config.value);
}
};
}
/**
* 可以配置的参数
*/
@Validated
public static class Config {
@NotEmpty
private String param;
@NotEmpty
private String value;
public @NotEmpty String getParam() {
return param;
}
public void setParam(@NotEmpty String param) {
this.param = param;
}
public @NotEmpty String getValue() {
return value;
}
public void setValue(@NotEmpty String value) {
this.value = value;
}
}
}
编写配置文件
yml
spring:
cloud:
gateway:
routes:
- id: bing-route
uri: https://cn.bing.com/ # 跳转地址
predicates:
- name: Path # 路径断言
args:
patterns: /search
- name: Query # 查询参数断言
args:
param: q # 参数名
regexp: haha # 正则表达式
# - Vip=user,jyh
- name: Vip # 自定义Vip断言
args:
param: user
value: jyh
四、Filter - 过滤器
4.1 路由重写 - RewritePath
![](https://i-blog.csdnimg.cn/direct/987e3ea58e1643aa807baaa0655cc6dc.png)
yml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates:
- name: Path # 路径断言
args: # 参数
pattern: /api/order/** # 匹配路径
matchTrailingSlash: true # 匹配尾部斜杠
filters:
- RewritePath=/api/order/?(?<segment>.*), /$\{segment} # 重写路径
RewritePath=/api/order/?(?<segment>.*), /$\{segment}
这个规则的意思是:
如果请求路径以 /api/order/
开头,且后面有任意内容(如 /api/order/123
),则会将路径重写为 /123
,即删除 /api/order/
部分,只保留后面的内容。
4.2 添加响应请求头 - AddResponseHeader
yml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates:
- name: Path # 路径断言
args: # 参数
pattern: /api/order/** # 匹配路径
matchTrailingSlash: true # 匹配尾部斜杠
filters:
- AddResponseHeader=X-Response-Abc, 123 # 添加响应请求头
![](https://i-blog.csdnimg.cn/direct/7ded8a1509954d95970a1e436eb9d996.png)
4.3 默认filter - Default Filters
所有路由规则都生效默认filter里面配置的规则
yml
spring:
cloud:
gateway:
routes:
- id: order-route
uri: lb://service-order # lb(LoadBalance)代表负载均衡
predicates:
- name: Path # 路径断言
args: # 参数
pattern: /api/order/** # 匹配路径
matchTrailingSlash: true # 匹配尾部斜杠
filters:
- RewritePath=/api/order/?(?<segment>.*), /$\{segment} # 重写路径
- id: product-route
uri: lb://service-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/product/?(?<segment>.*), /$\{segment} # 重写路径
default-filters: # 默认过滤器
- AddResponseHeader=X-Response-Abc, 123 # 添加响应头
4.4 全局filter - Global Filters
java
/**
* 全局过滤器 - 耗时统计
* @author jiangyiheng
*/
@Slf4j
@Component
public class RtGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String uri = exchange.getRequest().getURI().toString();
long start = System.currentTimeMillis();
log.info("请求开始【{}】开始时间:{}", uri, start);
// ======================以上是前置逻辑======================
Mono<Void> filter = chain.filter(exchange)
.doFinally((result)->{
// ======================以下是后置逻辑======================
long end = System.currentTimeMillis();
log.info("请求结束【{}】结束时间:{} ,耗时:{}ms", uri, end, end - start);
}); // 放行
return filter;
}
// 优先级
@Override
public int getOrder() {
return 0;
}
}
![](https://i-blog.csdnimg.cn/direct/c475cf8eaf5142c6b4fd40844078b7f4.png)
4.5 自定义过滤器
创建一个XxxGatewayFilterFactory
类继承AbstractNameValueGatewayFilterFactory
工厂
java
/**
* 自定义过滤器-令牌网关过滤器
*/
@Component
public class OneTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 每次响应前,添加一个一次性令牌,支持 uuid,jwt等各种格式
return chain.filter(exchange).then(Mono.fromRunnable(()->{
ServerHttpResponse response = exchange.getResponse();
String value = config.getValue();
if ("uuid".equalsIgnoreCase(value)) {
value = UUID.randomUUID().toString();
}
if ("jwt".equalsIgnoreCase(value)) {
value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
}
response.getHeaders().add(config.getName(), value);
}));
}
};
}
}
![](https://i-blog.csdnimg.cn/direct/5b1e0a1b60fa4be188cd510c3c088155.png)
五、CORS - 跨域处理
5.1 全局跨域
yml
spring:
cloud:
gateway:
globalcors:
cors-configurations:
'[/**]':
allowed-origin-patterns: '*' # 允许所有跨域
allowed-methods: '*' # 允许所有请求方式
allowed-headers: '*' # 允许所有头
5.2 局部跨域
yml
spring:
cloud:
gateway:
routes:
- id: cors_route
uri: https://example.org
predicates:
- Path=/service/**
metadata:
cors:
allowedOrigins: '*'
allowedMethods:
- GET
- POST
allowedHeaders: '*'
maxAge: 30
面试题 :微服务之间的调用经过网关吗?
微服务之间的调用一般不会经过网关 。网关主要用于处理外部请求,负责路由、认证、限流等功能。而微服务之间的通信通常通过服务发现 和负载均衡直接进行,不需要经过网关。然而,在某些特殊情况下,如果需要统一管理或控制,微服务之间的调用也可能通过网关。