Spring Cloud Gateway 实战:网关配置与 Sentinel 限流详解_spring gateway sentinel
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: 8010spring: 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: 8010spring: 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: 8010spring: application: name: gateway cloud: gateway: discovery: locator: enabled: true routes: - id: provider_route uri: http://localhost:8081 predicates: - Path=/provider/** filters: - StripPrefix=1
配置限流类
@Configurationpublic 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: 8010spring: application: name: gateway cloud: gateway: discovery: locator: enabled: true
配置限流类
@Configurationpublic 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 个请求