分类:架构与高并发 · 作者:管理员 · 发布于 2026-09-30 · 阅读 0 · 标签:Spring Cloud Gateway / Sentinel / 限流降级 / 微服务 / 网关

一、为什么需要网关层限流

微服务架构中,每个业务服务独立部署并通过内部 RPC 或 HTTP 通信。当突发流量(秒杀、热点事件、恶意爬虫)到来时,单个服务的崩溃可能通过级联效应拖垮整条调用链。传统的方式是在每个业务服务里嵌入限流逻辑——但这样做会带来几个问题:

  • 重复代码:每个服务都要配置限流阈值,维护成本高
  • 缺少全局视角:服务 A 限了 100 QPS,服务 B 也限了 100 QPS,但整体入口流量可能是 500 QPS,无法统一管控
  • 改配置要重启:限流阈值调整往往需要修改代码或配置后重新部署

在 API 网关层集中做限流降级,用一个统一的流量控制层保护下游所有微服务,是目前工业界的最佳实践。今天以 Spring Cloud Gateway + Sentinel 组合为例,从零搭建一个生产可用的网关限流方案。

二、技术选型为什么选 Sentinel

Java 生态的限流方案主要有三个:

  • Guava RateLimiter:单机令牌桶,只能用在进程内,不支持分布式
  • Redis + Lua 脚本:可实现分布式限流,但没有可视化控制台,规则调整需要开发介入
  • Sentinel:阿里开源的流量治理组件,支持限流/降级/熔断/热点防护,提供控制台动态规则推送,Spring Cloud Gateway 有官方适配

Sentinel 的核心优势在于:① 实时监控面板可以看到每个接口的 QPS、线程数、RT;② 动态规则推送不改代码、不重启;③ 丰富的流控模式(直接限流、关联限流、链路限流)。

三、Spring Cloud Gateway 集成 Sentinel(实战配置)

3.1 引入依赖

<!-- Spring Cloud Gateway 核心 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<!-- Sentinel 对 Spring Cloud Gateway 的适配 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel-gateway</artifactId>
    <version>2023.0.1.0</version>
</dependency>

<!-- Sentinel 数据源(Nacos 做动态规则持久化,可选) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

3.2 网关路由配置(application.yml)

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/api/product/**
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**

    sentinel:
      transport:
        dashboard: localhost:8800  # Sentinel 控制台地址
        port: 8719                 # 客户端与 dashboard 通信的端口
      eager: true                  # 饥饿加载,不设置的话要第一次请求才注册

这里的关键是 sentinel.transport.dashboard,指向 Sentinel 控制台的地址。eager: true 确保网关启动后就注册到控制台,而不是等到第一条请求进来。

3.3 定义网关限流规则(编码方式)

@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        // 订单服务限流:每 5 秒最多 100 个请求,超过后直接拒绝
        rules.add(new GatewayFlowRule("order-service")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                .setCount(100)
                .setIntervalSec(5)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT));

        // 商品服务限流:每 5 秒最多 200 个请求(商品浏览量大)
        rules.add(new GatewayFlowRule("product-service")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)
                .setCount(200)
                .setIntervalSec(5)
                .setGrade(RuleConstant.FLOW_GRADE_QPS));

        // API 分组限流:全部 API 加起来不超过 500 QPS
        rules.add(new GatewayFlowRule("api-group")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(500)
                .setIntervalSec(1)
                .setGrade(RuleConstant.FLOW_GRADE_QPS));

        GatewayRuleManager.loadRules(rules);
    }
}

这里有一个容易踩的坑:RESOURCE_MODE_ROUTE_ID 必须是路由 id(即配置文件里 route 的 id),不是路径。很多人误传为路径去配置,导致规则永远不生效。规则持久化建议用 Nacos 数据源,这样控制台修改后自动同步,不需要重启网关。

四、配置 Sentinel 控制台

Sentinel 控制台是一个独立的 Spring Boot 应用,从 GitHub 下载 release JAR 后直接启动:

java -Dserver.port=8800 -jar sentinel-dashboard-1.8.8.jar

启动后访问 http://localhost:8800(默认账号 sentinel/sentinel),就可以看到已注册的网关应用。在「机器列表」能看到网关实例,在「流控规则」可以动态增删改限流阈值——修改即刻生效,不需要重启网关。

生产中建议把 Sentinel 控制台挂在内网,用反向代理做 HTTPS 和认证,避免暴露到公网。

五、降级与熔断配置

限流解决的是"流量太多怎么办",降级解决的是"下游服务挂了怎么办"。Sentinel 的降级规则通过 DegradeRule 配置:

@PostConstruct
public void initDegradeRules() {
    Set<DegradeRule> rules = new HashSet<>();

    DegradeRule rule = new DegradeRule("order-service")
            .setGrade(RuleConstant.DEGRADE_GRADE_RT)   // 按响应时间降级
            .setCount(2000)                              // RT 超过 2000 ms
            .setTimeWindow(60);                          // 熔断 60 秒后自动恢复

    rules.add(rule);
    DegradeRuleManager.loadRules(rules);
}

降级后的默认响应是 Sentinel 返回一个 429 页面,但这在全栈分离架构中体验不好。更好的做法是自定义降级处理器:

@Component
public class CustomBlockRequestHandler implements BlockRequestHandler {

    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 429);
        result.put("message", "系统繁忙,请稍后重试");
        result.put("timestamp", System.currentTimeMillis());

        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(result));
    }
}

然后在配置类中注册:

@Bean
public BlockRequestHandler blockRequestHandler() {
    return new CustomBlockRequestHandler();
}

这样被限流或熔断时,前端收到的就是一个结构化的 JSON 响应,方便统一做错误处理。

六、生产踩坑记录

坑 1:Sentinel 规则不持久化,重启就丢

Sentinel 的规则默认存在内存里。网关重启后所有规则清零,流量直接淹到后端。解决方案:用 Nacos 或 Apollo 做数据源,Sentinel 在启动时从配置中心拉取规则,控制台的修改也写入配置中心。

坑 2:RESOURCE_MODE_ROUTE_ID 与路径的混淆

前面代码里已经强调了——路由 id 是你在 yml 里 routes: 下面写的 id: order-service 这个值,不是 /api/order/**。误写成路径会导致规则挂了但完全不生效,流量全部通过。

坑 3:控制台与网关不在同一时区

Sentinel 控制台的监控数据按时间聚合。如果控制台与网关的服务器时区不一致,看到的监控曲线会错位,排查问题时容易误判。务必将控制台 JVM 时区设为 -Duser.timezone=Asia/Shanghai。

坑 4:网关本身成为瓶颈

Sentinel 的限流判断是在网关线程中同步执行的。当规则数量庞大(几百条),每次请求都走一遍规则匹配,网关本身的 RT 会上升。建议规则数控制在 50 条以内,超过时用 API 分组(CUSTOM_API_NAME)聚合限流。

七、最佳实践总结

  1. 网关层限 80% 流量,服务层也保留兜底:网关限流主要防突发流量,但在网关被突破的情况下,业务服务自身也应有限流(如 Hystrix 或 Sentinel 注解方式),形成双层防护。
  2. 动态规则 > 静态编码:生产环境用 Nacos 数据源,规则可以通过控制台随时调整,不需要割接发布。
  3. 限流阈值先宽后严:上线第一天设一个较宽的阈值(如 1000 QPS),观察一周的业务峰值后逐步收紧到合理水位。
  4. 降级不等于摆烂:降级响应应该包含友好提示和预估恢复时间,而不是赤裸裸的 500 错误页。
  5. 监控告警闭环:Sentinel 控制台的监控只能看过去 5 分钟,长期趋势需要对接 Prometheus + Grafana。当限流触发率超过 5% 时触发告警,及时扩容或优化。

网关限流不是银弹,但它是微服务防护的第一道防线。Spring Cloud Gateway + Sentinel 的组合上手快、配置灵活、社区活跃,是中小团队落地流量治理的一个不错选择。