Spring Cloud Gateway 实战:网关配置与 Sentinel 限流详解

发布于:2025-06-30 ⋅ 阅读:(15) ⋅ 点赞:(0)

Spring Cloud Gateway 实战:网关配置与 Sentinel 限流详解

在微服务架构中,网关扮演着统一入口、负载均衡、安全认证、限流等多种角色。Spring Cloud Gateway 是 Spring Cloud 官方推出的新一代网关组件,相比于第一代 Netflix Zuul,性能更强、功能更丰富,且基于 Netty 和 WebFlux 开发,完全非阻塞、响应式。

本文将详细介绍 Spring Cloud Gateway 的基础配置、与 Nacos 注册中心的整合,以及如何基于 Sentinel 进行网关限流(包括路由限流和 API 分组限流)。


什么是 Spring Cloud Gateway

Spring Cloud Gateway 是 Spring Cloud 官方网关组件,属于第二代网关解决方案,替代了 Zuul。

优点

  • 基于 Netty + WebFlux,性能更高
  • 支持丰富的路由谓词和过滤器
  • 与 Spring Cloud 生态无缝集成

注意事项

  • 不兼容 Servlet(例如 SpringMVC)
  • 不支持 war 包部署,只能以 jar 形式运行

快速上手 Spring Cloud Gateway

引入依赖

一定不能引入 spring-boot-starter-web,因为它基于 Servlet,会导致冲突。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

application.yml 配置

以下为最简单的静态路由配置(不使用注册中心):

server:
  port: 8010
spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: provider_route
          uri: http://localhost:8081
          predicates:
            - Path=/provider/**
          filters:
            - StripPrefix=1
        - id: consumer_route
          uri: http://localhost:8181
          predicates:
            - Path=/consumer/**
          filters:
            - StripPrefix=1

说明

  • id:路由标识
  • uri:转发地址
  • predicates:请求匹配条件(如路径)
  • filters:过滤器(如去前缀)

整合 Nacos 服务注册中心

通过 Nacos 让 Gateway 自动发现服务,无需手动配置 routes。

引入依赖

<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>

修改配置

server:
  port: 8010
spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

开启后,Gateway 会自动根据 Nacos 上注册的服务自动创建路由。


Gateway 限流(基于 Sentinel)

在实际生产环境中,网关限流非常重要,可以防止后端服务被恶意或突发大流量冲击。

引入依赖

<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>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

基于路由 ID 的限流

配置 routes

server:
  port: 8010
spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: provider_route
          uri: http://localhost:8081
          predicates:
            - Path=/provider/**
          filters:
            - StripPrefix=1

配置限流类

@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;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("provider_route").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = (exchange, throwable) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("code", 0);
            map.put("msg", "被限流了");
            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(map));
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

被限流后,返回自定义 JSON 响应 {"code":0,"msg":"被限流了"}


基于 API 分组的限流

除了基于路由 ID 的限流,还可以针对 URL 前缀或具体路径进行分组限流。

修改配置

只开启服务发现,不写 routes。

server:
  port: 8010
spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

配置限流类

@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;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("provider_api1").setCount(1).setIntervalSec(1));
        rules.add(new GatewayFlowRule("provider_api2").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = (exchange, throwable) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("code", 0);
            map.put("msg", "被限流了");
            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(map));
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("provider_api1")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/provider/api1/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("provider_api2")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/provider/api2/demo1"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }
}

Controller 示例

@RestController
@RequestMapping("/provider")
public class DemoController {

    @GetMapping("/api1/demo1")
    public String demo1() {
        return "demo1";
    }

    @GetMapping("/api1/demo2")
    public String demo2() {
        return "demo2";
    }

    @GetMapping("/api2/demo1")
    public String demo3() {
        return "demo3";
    }

    @GetMapping("/api2/demo2")
    public String demo4() {
        return "demo4";
    }
}

限流效果

  • /provider/api1/** 前缀限流,每秒允许 1 个请求
  • /provider/api2/demo1 单接口限流,每秒允许 1 个请求

网站公告

今日签到

点亮在社区的每一天
去签到