SpringCloud Alibaba 核心组件解析:服务网关

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 为什么 --- 网关层限流的优势

  1. 请求入口拦截:不让无效请求进入微服务层,节省资源
  2. 统一管理:所有路由规则集中在网关配置,不需要每个微服务各自配
  3. 安全防护:恶意刷量在网关层就被拦截

6.3 怎么做 --- 完整实战

6.3.1 项目模块

模块 端口 说明
clooudalibaba-sentinel-gateway9528 9528 Sentinel + Gateway
cloudalibaba-provider-payment9001 9001 被代理的服务

6.3.2 步骤 ①:依赖

xml 复制代码
<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 步骤 ②:配置

yaml 复制代码
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 步骤 ③:核心配置类

java 复制代码
// 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 步骤 ④:验证

  1. 启动 Nacos → Gateway(9528) → Provider(9001)
  2. 快速连续访问 http://localhost:9528/pay/nacos/1
  3. QPS > 2 时返回自定义 JSON:
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 给过滤器