《预下单接口的高并发限流设计与实现》
要实现预下单接口 “总体 TPS≤5000、单个维度(如单个商户 / 用户)TPS≤1000” 的限流,需结合多维度限流规则、分布式限流协调和精准流量控制算法,确保在高并发场景下既不超总容量,也避免单个主体占用过多资源。以下是具体实现方案:
一、明确限流维度与规则
-
总体限流:预下单接口的总请求量(所有来源合计)每秒不超过 5000 次(TPS≤5000)。
-
单个维度限流:按 “商户 ID” 作为核心维度(支付场景中,“单个” 通常指单个商户,避免大商户挤占资源),单个商户的预下单请求每秒不超过 1000 次(TPS≤1000)。
二、选择限流工具与算法
1. 核心工具:Sentinel(适合分布式系统)
Sentinel 支持多维度限流、集群限流和动态规则配置,能同时满足 “总体” 和 “单个维度” 的限流需求,且可与 Spring Cloud 等框架无缝集成。
2. 限流算法:滑动窗口(避免固定窗口的临界值问题)
-
固定窗口(如 1 秒一个窗口)可能出现 “窗口边缘突发流量”(如前 0.9 秒 5000 次,后 0.1 秒 5000 次,实际 1 秒内 10000 次超限)。
-
滑动窗口将 1 秒拆分为 10 个 100ms 的小窗口,实时计算最近 1 秒的总请求量,精度更高,适合高并发场景。
三、具体实现步骤
1. 接口层埋点:标记限流资源
在预下单接口入口处,通过 Sentinel 的@SentinelResource标记资源,并指定限流异常处理逻辑(如返回 “系统繁忙”)。
@PostMapping("/preorder")
@SentinelResource(value = "preorder", blockHandler = "handlePreorderBlock")
public Result preorder(@RequestBody PreorderRequest request) {
String merchantId = request.getMerchantId(); // 提取商户ID,作为单个维度的标识
// 业务逻辑:生成预下单信息
return Result.success(preorderService.create(request));
}
// 限流时的降级处理
public Result handlePreorderBlock(PreorderRequest request, BlockException e) {
return Result.fail("当前请求繁忙,请稍后再试(错误码:LIMIT\_EXCEEDED)");
}
2. 配置多维度限流规则
通过 Sentinel Dashboard 或配置中心(如 Nacos)配置两层限流规则,先校验单个商户限流,再校验总体限流(顺序不可反,避免单个商户超限后占用总体配额)。
(1)单个商户维度限流(TPS≤1000)
-
资源名:
preorder(与接口埋点一致)。 -
限流类型:QPS(每秒请求数)。
-
限流模式:关联维度(通过
ContextUtil标记商户 ID)。 -
阈值:1000。
实现方式:在请求进入接口时,通过ContextUtil将商户 ID 注入 Sentinel 的上下文,作为限流维度的标识:
// 拦截器或AOP中,提取商户ID并标记上下文
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String merchantId = extractMerchantId(request); // 从请求参数/Header中提取商户ID
if (StringUtils.isNotBlank(merchantId)) {
// 标记上下文:维度为"merchant",值为merchantId
ContextUtil.enter("preorder", "merchant:" + merchantId);
}
return true;
}
在 Sentinel 中配置 “热点参数限流” 或 “上下文限流”,针对merchant:xxx维度设置 QPS=1000。
(2)总体维度限流(TPS≤5000)
-
资源名:
preorder。 -
限流类型:QPS。
-
限流模式:全局(不区分维度)。
-
阈值:5000。
配置方式:直接在 Sentinel 中对preorder资源设置全局 QPS=5000,无需额外维度标记,统计所有请求的总 QPS。
3. 分布式环境下的集群限流(关键)
如果预下单接口是集群部署(多实例),单机限流会导致总体超限(如 10 个实例,每个单机限 500,总体可能到 5000,但实际可能因流量不均到 6000)。需通过集群限流确保总 TPS 严格≤5000。
-
部署 Sentinel Token Server:作为集群限流的协调中心,统计所有实例的总请求量,动态分配每个实例的令牌数。
-
配置集群限流规则:在 Sentinel 中对
preorder资源开启 “集群限流”,设置总阈值 5000,由 Token Server 统一分配令牌(如 10 个实例,平均每个实例 500 令牌 / 秒,流量不均时动态调整)。
4. 动态调整与监控
-
动态规则:通过 Nacos 配置 Sentinel 规则,支持实时修改阈值(如大促时临时将总体 TPS 调整为 8000,单个商户调整为 1500)。
-
监控告警:通过 Sentinel Dashboard 监控实时 QPS、限流次数,设置告警阈值(如总体 QPS 接近 4500 时预警,单个商户 QPS 接近 900 时预警)。
-
日志埋点:记录限流日志(包含商户 ID、时间、限流类型),用于事后分析(如是否有商户频繁触发限流,需扩容或沟通)。
四、关键问题与优化
-
限流顺序:必须先校验单个商户限流,再校验总体限流。如果先校验总体,可能导致单个商户超限后,总体配额已被占用,其他商户无法请求。
-
空值处理:对未携带商户 ID 的请求(如匿名用户),可单独分配一个 “默认商户 ID”,并限制其 TPS(如默认商户限流 500 TPS,避免占满总体配额)。
-
突发流量缓冲:通过 “冷启动因子” 优化(如 Sentinel 的
warmUpPeriodSec),让限流阈值从低到高渐进式达到目标(如初始 1000 TPS,10 秒后升至 5000 TPS),避免瞬间流量冲击系统。
总结
通过 “单个商户维度限流(1000 TPS)+ 总体维度限流(5000 TPS)” 的双层规则,结合 Sentinel 的滑动窗口算法和集群限流能力,可精准控制预下单接口的流量。核心是通过上下文标记区分单个商户,再通过集群协调确保总体不超限,同时支持动态调整和监控,满足高并发场景下的稳定性需求。