SpringCloud Alibaba 核心组件解析:服务网关(Sentinel + Gateway 整合)
技术栈 :Spring Boot 3.2.0 + Spring Cloud Alibaba 2023.0.0.0-RC1 + Sentinel + Gateway + Nacos
6.1 是什么 --- Sentinel 网关限流
Gateway 负责路由转发,Sentinel 负责流量控制。两者整合后,可以在网关层 对进入微服务的流量进行统一限流和熔断,在请求到达微服务之前就拦截。
6.1.1 调用链路
客户端请求
│
▼
┌─────────────────────────────┐
│ Gateway (9528) │
│ ┌─────────────────────────┐ │
│ │ SentinelGatewayFilter │ │ ← 在这里拦截!超阈值直接拒绝
│ │ (网关层限流 QPS=2) │ │
│ └─────────┬───────────────┘ │
│ │ 通过限流 │
└────────────┼────────────────┘
▼
┌─────────────────────────────┐
│ Provider (9001) │
│ ┌─────────────────────────┐ │
│ │ @SentinelResource │ │ ← 服务层精细化限流
│ └─────────────────────────┘ │
└─────────────────────────────┘
6.1.2 网关限流 vs 服务限流
| 层级 |
组件 |
粒度 |
场景 |
| 网关层 |
GatewayFlowRule |
按路由或 API 分组 |
入口总流量控制 |
| 服务层 |
@SentinelResource |
按接口/资源 |
精细化流量控制 |
6.2 为什么 --- 网关层限流的优势
- 请求入口拦截:不让无效请求进入微服务层,节省资源
- 统一管理:所有路由规则集中在网关配置,不需要每个微服务各自配
- 安全防护:恶意刷量在网关层就被拦截
6.3 怎么做 --- 完整实战
6.3.1 项目模块
| 模块 |
端口 |
说明 |
clooudalibaba-sentinel-gateway9528 |
9528 |
Sentinel + Gateway |
cloudalibaba-provider-payment9001 |
9001 |
被代理的服务 |
6.3.2 步骤 ①:依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Sentinel 适配 Gateway 的桥接包 -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
6.3.3 步骤 ②:配置
server:
port: 9528
spring:
application:
name: cloudalibaba-sentinel-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
routes:
- id: pay_routh1
uri: http://localhost:9001 # 直接转发到 Provider
predicates:
- Path=/pay/**
sentinel:
log:
dir: D:/yangnan/Documents/SpringCloud/sentinel-log
6.3.4 步骤 ③:核心配置类
// clooudalibaba-sentinel-gateway9528/.../config/GatewayConfiguration.java
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(
ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
// ① 注册 Sentinel 异常处理器(最高优先级)
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(
viewResolvers, serverCodecConfigurer);
}
// ② 注册 Sentinel 全局过滤器(Order=-1,低于上方的处理器,但高于其他过滤器)
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
// ③ 应用启动后初始化规则
@PostConstruct
public void doInit() {
initBlockHandler();
}
private void initBlockHandler() {
// 网关流控规则:对路由 pay_routh1 限制每秒最多 2 个请求
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("pay_routh1")
.setCount(2) // QPS 阈值
.setIntervalSec(1)); // 统计间隔 1 秒
GatewayRuleManager.loadRules(rules);
// 自定义限流响应(返回 JSON 而不是默认 HTML)
BlockRequestHandler handler = (exchange, t) -> {
Map<String, String> map = new HashMap<>();
map.put("errorCode",
HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
map.put("errorMessage",
"请求太过频繁,系统忙不过来,触发限流(sentinel+gateway整合Case)");
return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
};
GatewayCallbackManager.setBlockHandler(handler);
}
}
配置解读:
| 组件 |
作用 |
SentinelGatewayBlockExceptionHandler |
处理限流时抛出的 BlockException |
SentinelGatewayFilter |
拦截每个请求,执行限流规则判断 |
GatewayFlowRule |
网关级流控规则,按 routeId 限流 |
BlockRequestHandler |
自定义被限流时返回的错误信息(JSON 格式) |
6.3.5 步骤 ④:验证
- 启动 Nacos → Gateway(9528) → Provider(9001)
- 快速连续访问
http://localhost:9528/pay/nacos/1
- QPS > 2 时返回自定义 JSON:
{
"errorCode": "Too Many Requests",
"errorMessage": "请求太过频繁,系统忙不过来,触发限流(sentinel+gateway整合Case)"
}
6.4 深入原理 --- 网关限流实现流程
请求到达 Gateway
│
▼
SentinelGatewayFilter.doFilter()
│
├→ 提取 routeId(如 "pay_routh1")
├→ 匹配 GatewayFlowRule(QPS=2)
├→ 判断是否超阈值
│
├→ 未超:chain.filter() → 转发到 Provider ✅
└→ 超了:throw BlockException → BlockRequestHandler → 返回 JSON ❌
6.5 网关限流 vs 服务限流对比
| 维度 |
网关限流 |
服务限流 |
| 实现方式 |
GatewayFlowRule + SentinelGatewayFilter |
@SentinelResource |
| 配置位置 |
网关代码中硬编码或动态配置 |
控制台或注解 |
| 粒度 |
按路由/API 分组 |
按资源名/URL |
| 优势 |
统一入口,减少无效流量进入 |
精细化控制 |
| 劣势 |
无法感知服务内部逻辑 |
需要每个服务单独配置 |
6.6 面试题
Q1:网关层限流和服务层限流如何选择?
答 :两者不是替代关系而是互补关系。网关层做粗粒度总流量控制(如每秒不超过 1000),服务层做细粒度接口控制(如秒杀接口单独限制)。双层防护更安全。
Q2:Sentinel 网关限流和 Resilience4J RequestRateLimiter 有什么区别?
答:Sentinel 需要独立部署 Dashboard,提供实时监控和规则热更新;Resilience4J RequestRateLimiter 是 Gateway 内置过滤器,配置简单但功能有限。
6.7 踩坑指南
| 坑 |
现象 |
原因 |
解决 |
| 🔴 过滤器优先级不对 |
限流不生效 |
SentinelGatewayFilter 优先级低于其他过滤器 |
确保 @Order(-1) |
| 🔴 异常处理器不工作 |
限流后返回默认 HTML 错误页 |
SentinelGatewayBlockExceptionHandler 未注册或优先级不够 |
确保 @Order(Ordered.HIGHEST_PRECEDENCE) |
| 🔴 sentinel-gateway-adapter 版本冲突 |
NoClassDefFoundError |
adapter 版本与 sentinel-core 不匹配 |
由 Spring Cloud Alibaba BOM 统一管理版本,不要手动指定 |
6.8 章节总结
| 要点 |
说明 |
| 三个关键 Bean |
SentinelGatewayFilter(拦截请求)+ SentinelGatewayBlockExceptionHandler(处理异常)+ GatewayFlowRule(配置规则) |
| 规则配置 |
GatewayFlowRule("routeId").setCount(N).setIntervalSec(S) |
| 自定义响应 |
BlockRequestHandler 返回 JSON,替代默认 HTML |
| 双层防护 |
网关层粗粒度 + 服务层细粒度,组合使用 |
| @Order 很重要 |
HIGHEST_PRECEDENCE 给异常处理器,-1 给过滤器 |