跳到主要内容

API 网关流量控制

Sentinel 支持对 Spring Cloud Gateway、Zuul 1.x 等 API 网关进行流量控制,可以在网关层实现对路由和 API 分组的精细化限流。

为什么需要网关流量控制

在微服务架构中,API 网关是系统的统一入口,所有的外部请求都需要经过网关。在网关层进行流量控制有以下几个优势:

1. 统一管控

所有的请求都经过网关,可以在网关层统一配置和管理限流规则,无需在每个微服务中单独配置。

2. 提前拦截

在网关层拦截超量请求,避免无效请求进入后端服务,节省系统资源。

3. 按路由限流

可以根据不同的路由或 API 分组设置不同的限流策略,实现精细化的流量控制。

4. 支持请求属性限流

可以根据请求中的属性(如 HTTP 头、URL 参数、客户端 IP 等)进行限流。

基本概念

GatewayFlowRule(网关流控规则)

网关流控规则是专门为 API 网关设计的限流规则,支持以下特性:

  • 按 Route ID 限流:对 Spring Cloud Gateway 或 Zuul 中定义的路由进行限流
  • 按 API 分组限流:对自定义的 API 分组进行限流
  • 按请求属性限流:根据请求头、URL 参数、客户端 IP 等属性进行限流

ApiDefinition(API 分组定义)

API 分组可以将多个路由或 URL 模式归类为一个逻辑分组,然后对这个分组进行统一的限流。

例如,可以将所有用户相关的接口定义为 user-api 分组,然后对这个分组设置统一的限流规则。

Spring Cloud Gateway 整合

添加依赖

<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2022.0.0.0</version>
</dependency>

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

基本配置

spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/users/**
- id: order-service
uri: lb://order-service
predicates:
- Path=/api/orders/**
sentinel:
transport:
dashboard: localhost:8080
eager: true

配置网关过滤器

Sentinel 会自动识别 Spring Cloud Gateway 中定义的路由,并将其作为资源进行管理。

import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;

@Configuration
public class GatewayConfiguration {

/**
* 配置 Sentinel 网关过滤器
*/
@Bean
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}

/**
* 配置限流异常处理器
*/
@Bean
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler();
}

/**
* 自定义限流响应
*/
@PostConstruct
public void init() {
GatewayCallbackManager.setBlockHandler((exchange, t) -> {
ServerWebExchange serverWebExchange = exchange;
serverWebExchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
serverWebExchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

String result = "{\"code\":429,\"message\":\"系统繁忙,请稍后重试\"}";
DataBuffer buffer = serverWebExchange.getResponse()
.bufferFactory()
.wrap(result.getBytes());

return serverWebExchange.getResponse().writeWith(Mono.just(buffer));
});
}
}

配置网关流控规则

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class GatewayRuleConfig {

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

// 对路由 user-service 进行限流
rules.add(new GatewayFlowRule("user-service")
.setCount(100) // QPS 阈值
.setIntervalSec(1) // 统计窗口:1 秒
);

// 对路由 order-service 进行限流,支持突发流量
rules.add(new GatewayFlowRule("order-service")
.setCount(50) // QPS 阈值
.setIntervalSec(1)
.setBurst(20) // 突发流量额外容量
);

// 加载规则
GatewayRuleManager.loadRules(rules);
}
}

网关流控规则属性

属性说明默认值
resource资源名(Route ID 或 API 分组名)必填
count限流阈值必填
intervalSec统计时间窗口(秒)1
burst应对突发请求额外容量0
controlBehavior流控效果(0=快速失败,2=匀速排队)0
burstRequestRate匀速排队模式下的突发请求数-
maxQueueingTimeoutMs最大排队等待时间(ms)0
paramItem参数限流配置-

按请求属性限流

可以根据请求中的属性进行精细化限流:

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;

// 根据请求头限流
GatewayFlowRule headerRule = new GatewayFlowRule("user-service")
.setCount(10)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
.setFieldName("X-User-Id") // 根据用户 ID 限流
);

// 根据 URL 参数限流
GatewayFlowRule paramRule = new GatewayFlowRule("user-service")
.setCount(5)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("userId")
);

// 根据客户端 IP 限流
GatewayFlowRule ipRule = new GatewayFlowRule("user-service")
.setCount(20)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
);

// 根据 Cookie 限流
GatewayFlowRule cookieRule = new GatewayFlowRule("user-service")
.setCount(10)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
.setFieldName("sessionId")
);

API 分组定义

可以将多个路由归类为一个 API 分组:

import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class ApiDefinitionConfig {

@PostConstruct
public void init() {
Set<ApiDefinition> definitions = new HashSet<>();

// 定义 user-api 分组,匹配所有用户相关的接口
ApiDefinition userApi = new ApiDefinition("user-api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 精确匹配
add(new ApiPathPredicateItem()
.setPattern("/api/users/info")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
// 前缀匹配
add(new ApiPathPredicateItem()
.setPattern("/api/users/**")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
// 正则匹配
add(new ApiPathPredicateItem()
.setPattern("/api/user/.*")
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_REGEX));
}});

definitions.add(userApi);

// 加载 API 分组定义
GatewayApiDefinitionManager.loadApiDefinitions(definitions);

// 对 API 分组进行限流
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("user-api")
.setCount(200)
.setIntervalSec(1)
);
GatewayRuleManager.loadRules(rules);
}
}

Zuul 1.x 整合

添加依赖

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-zuul-adapter</artifactId>
<version>1.8.8</version>
</dependency>

配置过滤器

import com.alibaba.csp.sentinel.adapter.zuul.fallback.DefaultRequestOriginParser;
import com.alibaba.csp.sentinel.adapter.zuul.fallback.ZuulBlockFallbackManager;
import com.alibaba.csp.sentinel.adapter.zuul.filters.SentinelZuulPreFilter;
import com.alibaba.csp.sentinel.adapter.zuul.filters.SentinelZuulPostFilter;
import com.alibaba.csp.sentinel.adapter.zuul.filters.SentinelZuulErrorFilter;
import com.netflix.zuul.ZuulFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ZuulConfig {

@Bean
public ZuulFilter sentinelZuulPreFilter() {
return new SentinelZuulPreFilter();
}

@Bean
public ZuulFilter sentinelZuulPostFilter() {
return new SentinelZuulPostFilter();
}

@Bean
public ZuulFilter sentinelZuulErrorFilter() {
return new SentinelZuulErrorFilter();
}

/**
* 自定义限流降级响应
*/
@PostConstruct
public void init() {
ZuulBlockFallbackManager.registerProvider((request, route, ex) -> {
return new BlockResponse(429, "系统繁忙,请稍后重试", route);
});
}
}

配置网关流控规则

Zuul 的网关流控规则配置方式与 Spring Cloud Gateway 相同:

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.Set;

@Configuration
public class ZuulRuleConfig {

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

// 对路由进行限流
rules.add(new GatewayFlowRule("user-service")
.setCount(100)
.setIntervalSec(1)
);

GatewayRuleManager.loadRules(rules);
}
}

动态规则配置

网关流控规则同样支持动态配置源:

Nacos 数据源

spring:
cloud:
sentinel:
datasource:
gw-flow:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-gw-flow-rules
groupId: SENTINEL_GROUP
rule-type: gw-flow
gw-api-group:
nacos:
server-addr: localhost:8848
dataId: ${spring.application.name}-gw-api-group-rules
groupId: SENTINEL_GROUP
rule-type: gw-api-group

规则 JSON 格式:

[
{
"resource": "user-service",
"count": 100,
"intervalSec": 1
},
{
"resource": "order-service",
"count": 50,
"intervalSec": 1,
"burst": 20
}
]

API 分组定义 JSON 格式:

[
{
"apiName": "user-api",
"predicateItems": [
{
"pattern": "/api/users/**",
"matchStrategy": 1
}
]
}
]

与控制台集成

Sentinel 控制台支持网关流控规则的可视化管理:

  1. 在控制台的"网关流控规则"页面可以管理规则
  2. 在"API 分组管理"页面可以管理 API 分组定义
  3. 实时监控网关的流量情况

控制台网关功能

控制台提供了专门的网关管理模块:

  • 网关流控规则:管理路由和 API 分组的限流规则
  • API 分组管理:定义和管理 API 分组
  • 网关实时监控:查看网关的实时流量数据

最佳实践

1. 合理规划限流粒度

根据业务特点选择合适的限流粒度:

  • 路由级别:适合对整个服务进行限流
  • API 分组级别:适合对一组相关接口进行统一限流
  • 请求属性级别:适合需要精细化限流的场景

2. 配置合理的阈值

  • 通过压测确定网关的处理能力
  • 设置阈值时留有余量(建议 70%-80%)
  • 核心接口和非核心接口设置不同的阈值

3. 提供友好的限流响应

GatewayCallbackManager.setBlockHandler((exchange, t) -> {
ServerWebExchange serverWebExchange = exchange;
serverWebExchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
serverWebExchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

// 返回友好的错误信息
Map<String, Object> result = new HashMap<>();
result.put("code", 429);
result.put("message", "系统繁忙,请稍后重试");
result.put("timestamp", System.currentTimeMillis());

String json = JSON.toJSONString(result);
DataBuffer buffer = serverWebExchange.getResponse()
.bufferFactory()
.wrap(json.getBytes());

return serverWebExchange.getResponse().writeWith(Mono.just(buffer));
});

4. 监控与告警

  • 监控网关的 QPS 和限流触发情况
  • 设置告警阈值,及时发现异常流量
  • 记录限流日志,便于问题排查

注意事项

1. 网关性能

网关流控会增加一定的性能开销,建议:

  • 合理设置规则数量
  • 避免过于复杂的参数匹配
  • 监控网关的资源使用情况

2. 规则更新

规则更新可能有短暂的延迟,不支持毫秒级别的实时生效。

3. 集群限流

如果需要对网关集群进行统一的限流,可以结合 Sentinel 的集群限流功能。

下一步

  • 集群限流 - 学习分布式环境下的集群维度流量控制
  • 控制台 - 掌握 Sentinel 控制台的使用