返利机器人的流量削峰策略:Java Sentinel实现接口限流与熔断降级的全链路保护

大家好,我是 微赚淘客系统3.0 的研发者省赚客!

微赚淘客系统3.0 的返利机器人每日需处理数百万次用户请求,尤其在大促期间(如双11),外部平台接口(淘宝联盟、京东联盟)响应延迟飙升,若无保护机制,将引发线程堆积、服务雪崩。我们引入 Alibaba Sentinel 实现 QPS 限流、慢调用熔断、异常比例降级 三位一体防护,保障核心链路稳定。本文展示基于注解与硬编码的完整 Java 实现。

1. Sentinel 核心依赖与初始化

Maven 引入:

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

配置文件启用本地规则持久化(避免重启丢失):

# application.yml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: sentinel.juwatech.cn:8080
      datasource:
        ds1:
          nacos:
            server-addr: nacos.juwatech.cn:8848
            data-id: rebate-sentinel-rules
            group-id: DEFAULT_GROUP
            rule-type: flow

2. 接口级 QPS 限流(注解方式)

对高频查券接口限制单机 100 QPS:

// juwatech.cn.robot.controller.CouponController.java
@RestController
public class CouponController {

    @Autowired
    private CouponService couponService;

    @GetMapping("/api/coupon/query")
    @SentinelResource(
        value = "queryCoupon",
        blockHandler = "handleQueryBlocked",
        fallback = "queryFallback"
    )
    public ResponseEntity<CouponResult> queryCoupon(@RequestParam String itemId) {
        return ResponseEntity.ok(couponService.query(itemId));
    }

    // 限流/熔断时触发
    public ResponseEntity<CouponResult> handleQueryBlocked(String itemId, BlockException ex) {
        log.warn("查券接口被限流或熔断: {}", ex.getClass().getSimpleName());
        return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
            .body(CouponResult.error("系统繁忙,请稍后再试"));
    }

    // 业务异常兜底(非 Sentinel 触发)
    public ResponseEntity<CouponResult> queryFallback(String itemId, Throwable t) {
        log.error("查券接口异常", t);
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
            .body(CouponResult.error("服务暂时不可用"));
    }
}

3. 硬编码定义流控规则

启动时动态注册规则(适用于无注解场景):

// juwatech.cn.robot.sentinel.SentinelRuleInitializer.java
@Component
public class SentinelRuleInitializer implements CommandLineRunner {

    @Override
    public void run(String... args) {
        // 1. 流控规则:按 QPS
        FlowRule flowRule = new FlowRule("applyRebate")
            .setCount(50) // 单机阈值50 QPS
            .setGrade(RuleConstant.FLOW_GRADE_QPS)
            .setLimitApp("default");
        FlowRuleManager.loadRules(Collections.singletonList(flowRule));

        // 2. 熔断规则:慢调用比例 > 60% 且 RT > 800ms,熔断10秒
        DegradeRule degradeRule = new DegradeRule("callTaoBaoApi")
            .setGrade(RuleConstant.DEGRADE_GRADE_RT)
            .setCount(800) // 毫秒
            .setSlowRatioThreshold(0.6)
            .setMinRequestAmount(5)
            .setStatIntervalMs(10000)
            .setTimeWindow(10); // 熔断时长(秒)
        DegradeRuleManager.loadRules(Collections.singletonList(degradeRule));

        // 3. 异常比例熔断:错误率 > 50%,熔断15秒
        DegradeRule errorRule = new DegradeRule("callJdApi")
            .setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)
            .setCount(0.5)
            .setMinRequestAmount(10)
            .setTimeWindow(15);
        DegradeRuleManager.loadRules(Arrays.asList(degradeRule, errorRule));
    }
}

4. 资源埋点与外部调用保护

在调用第三方 API 处显式定义资源:

// juwatech.cn.robot.service.TaoBaoApiService.java
@Service
public class TaoBaoApiService {

    public TaobaoResponse callApi(String params) {
        try (Entry entry = SphU.entry("callTaoBaoApi")) {
            // 实际 HTTP 调用
            return restTemplate.postForObject("https://eco.taobao.com/api", params, TaobaoResponse.class);
        } catch (BlockException ex) {
            // 被 Sentinel 阻断
            throw new ServiceBlockedException("淘宝API调用被限流", ex);
        } catch (Exception ex) {
            // 业务异常,会被 fallback 捕获
            throw new RuntimeException("调用淘宝API失败", ex);
        }
    }
}

5. 自定义 BlockExceptionHandler

统一返回 JSON 格式限流提示:

// juwatech.cn.robot.sentinel.GlobalBlockHandler.java
@Component
public class GlobalBlockHandler implements UrlBlockHandler {

    @Override
    public void blocked(HttpServletRequest request, HttpServletResponse response, 
                        BlockException ex) throws IOException {
        response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
        response.setContentType("application/json;charset=UTF-8");
        String msg = "请求过于频繁,请稍后再试";
        if (ex instanceof DegradeException) {
            msg = "服务暂时不可用,请稍后重试";
        }
        response.getWriter().write(JSON.toJSONString(Map.of("code", 429, "msg", msg)));
    }
}

并在配置中注册:

// juwatech.cn.robot.config.SentinelWebConfig.java
@Configuration
public class SentinelWebConfig {

    @PostConstruct
    public void init() {
        WebCallbackManager.setUrlBlockHandler(new GlobalBlockHandler());
    }
}

6. 热点参数限流(防刷单)

针对 userId 参数做细粒度控制:

// juwatech.cn.robot.service.RebateApplyService.java
public void applyRebate(String userId, String itemId) {
    try (Entry entry = SphU.entry("applyRebate", EntryType.OUT, 1, userId)) {
        // 执行申请逻辑
        doApply(userId, itemId);
    } catch (BlockException ex) {
        throw new UserRequestTooFrequentException("您操作太频繁,请稍后再试");
    }
}

// 初始化热点规则
@PostConstruct
public void initHotParamRule() {
    ParamFlowRule rule = new ParamFlowRule("applyRebate")
        .setParamIdx(0) // 第一个参数(userId)
        .setCount(5)    // 单个用户每秒最多5次
        .setDurationInSec(1);
    ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
}

7. 监控与告警集成

通过 Sentinel Dashboard 实时查看指标,并对接 Prometheus:

// 暴露指标端点
management:
  endpoints:
    web:
      exposure:
        include: sentinel

当熔断触发时,自动推送企业微信告警:

// juwatech.cn.robot.alert.SentinelAlertListener.java
@EventListener
public void onDegradeEvent(DegradeEvent event) {
    alertService.send("【熔断告警】资源:" + event.getResource() + 
                     ",原因:" + event.getOrigin());
}

上线后,系统在双11峰值 QPS 12,000 下保持 99.95% 可用性,外部 API 故障未导致服务雪崩。

本文著作权归 微赚淘客系统3.0 研发团队,转载请注明出处!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐