Sentinel从入门到实战:流量卫兵守护你的微服务

62 阅读12分钟

Sentinel从入门到实战:流量卫兵守护你的微服务

一、为什么需要限流组件?

在分布式系统和微服务架构日益普及的今天,流量控制已经成为保障系统稳定性的核心手段。让我们先看一个真实的生产案例:

1.1 真实案例:某电商平台崩溃事故

2023年双11大促期间,某中型电商平台在秒杀活动开启后5分钟内系统全面崩溃。事后分析发现:

  • 流量峰值:正常QPS约500,秒杀开始瞬间飙升至50000+
  • 系统表现:所有服务实例CPU 100%,内存溢出,数据库连接池耗尽
  • 影响范围:不仅秒杀服务崩溃,连带影响首页、浏览、下单等所有核心功能
  • 直接损失:约300万交易额无法完成,品牌声誉受损

图1:无限流保护的系统架构 - 流量冲击场景

如上图所示,没有限流保护的系统面临多重问题:

① 资源耗尽问题 当突发流量到来时,系统会按照"先来先服务"的原则处理请求。流量超出系统承载能力后:

  • CPU持续高负载,上下文切换频繁
  • 内存快速分配导致GC压力增大,最终OOM
  • 数据库连接池被占满,新请求无法获取连接

② 雪崩效应 微服务架构中,服务间存在依赖关系。当下游服务因流量过大而响应缓慢或失败时:

  • 上游服务持有的连接/线程无法释放,资源逐渐耗尽
  • 故障会沿着调用链向上传播,最终导致整个系统崩溃
  • 一个服务的故障可能影响整个业务链路

③ 用户体验恶化

  • 正常用户的请求无法得到及时响应
  • 页面长时间加载或直接报错
  • 用户流失,信任度下降

1.2 传统解决方案的局限性

面对流量冲击,传统方案通常包括:

方案优点缺点
垂直扩容简单直接成本高,有上限,无法应对瞬时流量
缓存减轻数据库压力缓存击穿/穿透/雪崩问题,热点数据难以处理
消息队列削峰填谷增加系统复杂度,实时性下降
人工限流灵活性高响应慢,无法精确控制,容易误操作

这些方案要么成本过高,要么效果有限,要么需要大量人力投入。我们需要一个更加智能化、自动化的流量控制解决方案。

二、Sentinel简介

2.1 Sentinel是什么?

Sentinel是阿里巴巴开源的一款面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制熔断降级系统负载保护等多个维度来帮助您保障微服务的稳定性。

2.2 核心特性

① 丰富的应用场景

  • 秒杀系统:瞬间大流量限流
  • 消息削峰:平滑处理突发流量
  • 熔断降级:保护核心服务不被拖垮
  • 实时监控:全方位流量监控大盘

② 完备的实时监控 Sentinel提供实时的监控功能,可以监控:

  • QPS(每秒查询数)
  • RT(响应时间)
  • 成功率
  • 拒绝数

③ 广泛的开源生态 Sentinel提供与主流框架的整合:

  • Spring Cloud / Spring Boot
  • Dubbo
  • gRPC
  • RocketMQ

④ 完善的SPI扩展点 Sentinel提供简单易用、完善的SPI扩展接口,可以通过实现扩展接口来快速定制逻辑。

三、Sentinel核心原理

3.1 责任链模式设计

Sentinel的核心工作原理基于责任链模式,通过一系列的Slot(槽位)串联成一个处理链。

图3:Sentinel核心工作原理

当请求进入时,会依次经过以下Slot:

① NodeSelectorSlot

  • 负责收集资源的调用路径
  • 构建树状结构的调用链路
  • 为每个资源创建DefaultNode

② ClusterBuilderSlot

  • 构建ClusterNode
  • 统计集群维度某个资源的调用情况
  • 用于存储聚合后的统计信息

③ LogSlot

  • 记录异常日志
  • 便于问题排查和定位

④ StatisticSlot

  • 实时统计指标的Slot
  • 记录秒级、分钟级的指标数据
  • 为后续的规则判断提供数据支撑

⑤ AuthoritySlot

  • 黑白名单控制
  • 基于来源的流量控制

⑥ FlowSlot

  • 流量控制Slot
  • 根据配置的限流规则进行判断
  • 超出阈值则抛出FlowException

⑦ DegradeSlot

  • 熔断降级Slot
  • 根据熔断规则判断是否需要熔断
  • 触发熔断则抛出DegradeException

3.2 核心概念

① 资源(Resource) 资源是Sentinel的核心概念,可以是:

  • Java代码中的一段代码
  • 一个接口
  • 一个方法
// 定义资源方式一:使用@SentinelResource注解
@SentinelResource(value = "getUserInfo", blockHandler = "handleBlock")
public User getUserInfo(Long id) {
    return userService.getUser(id);
}

// 定义资源方式二:使用try-catch
try (Entry entry = SphU.entry("getUserInfo")) {
    return userService.getUser(id);
} catch (BlockException e) {
    return handleBlock(e);
}

② 规则(Rule) Sentinel支持多种规则类型:

规则类型说明配置参数
FlowRule流量控制规则resource, limitApp, grade, count, strategy, controlBehavior
DegradeRule熔断降级规则resource, grade, count, timeWindow, minRequestAmount
AuthorityRule访问控制规则resource, limitApp, strategy
ParamFlowRule热点参数规则resource, paramIdx, grade, count, durationInSec

③ 上下文(Context) Context保存了调用链路的元数据,包括:

  • 入口节点(EntranceNode)
  • 当前节点(CurrentNode)
  • 调用来源(Origin)

四、流量控制详解

4.1 限流流程

Sentinel的限流处理流程如下图所示:

图4:Sentinel限流处理流程

4.2 限流维度

① QPS限流 每秒查询数限流,适用于:

  • API接口限流
  • 防止接口被刷
  • 保护下游服务
// QPS限流配置
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("getUserInfo");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // QPS维度
rule.setCount(100); // 每秒最多100个请求
rule.setStrategy(RuleConstant.STRATEGY_DIRECT); // 直接拒绝
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 默认行为
rules.add(rule);
FlowRuleManager.loadRules(rules);

② 线程数限流 并发线程数限流,适用于:

  • 业务处理时间长
  • 需要限制并发量的场景
// 线程数限流配置
FlowRule rule = new FlowRule();
rule.setResource("processOrder");
rule.setGrade(RuleConstant.FLOW_GRADE_THREAD); // 线程数维度
rule.setCount(10); // 最多10个线程并发处理

4.3 限流策略

① 直接拒绝(Default) 默认策略,超出阈值直接拒绝请求:

rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
// 抛出FlowException: Blocked by Sentinel (flow limiting)

② Warm Up(预热) 从初始阈值缓慢上升到最大阈值:

rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
rule.setCount(100); // 最大阈值
rule.setWarmUpPeriodSec(10); // 预热时长10秒
// 实际效果:前10秒从 100/3 ≈ 33 逐渐增加到100

适用于:

  • 秒杀系统预热
  • 缓存预热
  • 系统启动阶段流量控制

③ 排队等待(Throttling) 请求在队列中排队,超时则拒绝:

rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER);
rule.setCount(100); // 每秒100个请求
rule.setMaxQueueingTimeMs(500); // 最大排队等待时间500ms

适用于:

  • 消息削峰填谷
  • 请求可以适当延迟处理的场景

4.4 关联限流

当关联的资源达到阈值时,对当前资源限流:

// 当userLogin资源QPS超过50时,限制getUserInfo
rule.setStrategy(RuleConstant.STRATEGY_RELATE);
rule.setRefResource("userLogin");
rule.setCount(50);

适用场景:

  • 登录接口压力大时,限制其他非核心接口
  • 支付接口压力大时,限制下单接口

五、熔断降级详解

5.1 熔断机制

熔断器模式(Circuit Breaker Pattern)是一种保护性设计模式,Sentinel实现了完整的熔断降级机制。

图5:Sentinel熔断降级机制

熔断器有三种状态:

① Closed(关闭状态)

  • 正常状态,请求正常通过
  • 监控失败率和响应时间
  • 异常达到阈值时,转换为Open状态

② Open(开启状态)

  • 熔断状态,直接拒绝所有请求
  • 持续熔断时长(timeWindow)结束后进入Half-Open状态

③ Half-Open(半开状态)

  • 探测状态,允许部分请求通过
  • 如果请求成功,说明服务已恢复,转为Closed状态
  • 如果请求失败,说明服务仍未恢复,转回Open状态

5.2 熔断策略

① 慢调用比例(SLOW_REQUEST_RATIO)

当资源的响应时间超过最大RT时,被标记为慢调用。慢调用比例超过阈值时触发熔断:

List<DegradeRule> rules = new ArrayList<>();
DegradeRule rule = new DegradeRule();
rule.setResource("orderService");
rule.setGrade(RuleConstant.DEGRADE_GRADE_SLOW_REQUEST_RATIO);
rule.setCount(500); // 慢调用阈值:RT > 500ms
rule.setSlowRatioThreshold(0.5); // 慢调用比例阈值:50%
rule.setTimeWindow(10); // 熔断时长:10秒
rule.setMinRequestAmount(5); // 最小请求数:5
rule.setStatIntervalMs(1000); // 统计时长:1秒
rules.add(rule);
DegradeRuleManager.loadRules(rules);

② 异常比例(EXCEPTION_RATIO)

当资源的异常比例超过阈值时触发熔断:

DegradeRule rule = new DegradeRule();
rule.setResource("paymentService");
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
rule.setCount(0.5); // 异常比例阈值:50%
rule.setTimeWindow(10); // 熔断时长:10秒
rule.setMinRequestAmount(5); // 最小请求数:5

③ 异常数(EXCEPTION_COUNT)

当资源的异常数超过阈值时触发熔断:

DegradeRule rule = new DegradeRule();
rule.setResource("smsService");
rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
rule.setCount(10); // 异常数阈值:10个
rule.setTimeWindow(10); // 熔断时长:10秒
rule.setMinRequestAmount(5); // 最小请求数:5

5.3 降级处理

当请求被限流或熔断时,需要进行降级处理:

@SentinelResource(
    value = "getUserInfo",
    blockHandler = "handleBlock",     // 限流/熔断时的处理
    fallback = "handleFallback"        // 异常时的处理
)
public User getUserInfo(Long id) {
    return userService.getUser(id);
}

// 限流/熔断处理
public User handleBlock(Long id, BlockException e) {
    // 返回默认值或缓存数据
    return User.getDefaultUser();
}

// 异常处理
public User handleFallback(Long id, Throwable e) {
    log.error("获取用户信息异常", e);
    return User.getDefaultUser();
}

六、生产实战案例

6.1 电商秒杀系统

以下是一个电商秒杀系统使用Sentinel的完整案例:

图6:生产实战:电商秒杀系统Sentinel应用架构

场景描述:

  • 正常流量:约2000 QPS
  • 秒杀流量:峰值可达50000+ QPS
  • 核心需求:保护库存服务不被击垮

配置方案:

@Configuration
public class SentinelConfig {

    @PostConstruct
    public void initRules() {
        initSeckillFlowRules();
        initOrderDegradeRules();
        initStockFlowRules();
    }

    /**
     * 秒杀接口限流规则
     */
    private void initSeckillFlowRules() {
        List<FlowRule> rules = new ArrayList<>();

        // 用户维度限流
        FlowRule userRule = new FlowRule();
        userRule.setResource("seckill:userId");
        userRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        userRule.setCount(5); // 单用户每秒最多5次请求
        userRule.setParamIdx(0); // 第一个参数作为userId
        rules.add(userRule);

        // 接口维度限流
        FlowRule apiRule = new FlowRule();
        apiRule.setResource("/api/seckill/doSeckill");
        apiRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        apiRule.setCount(10000); // 接口整体限制10000 QPS
        apiRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
        apiRule.setWarmUpPeriodSec(10); // 10秒预热
        rules.add(apiRule);

        FlowRuleManager.loadRules(rules);
    }

    /**
     * 订单服务熔断规则
     */
    private void initOrderDegradeRules() {
        List<DegradeRule> rules = new ArrayList<>();

        DegradeRule rule = new DegradeRule();
        rule.setResource("orderService:createOrder");
        rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        rule.setCount(0.3); // 异常比例30%
        rule.setTimeWindow(10); // 熔断10秒
        rule.setMinRequestAmount(10);
        rules.add(rule);

        DegradeRuleManager.loadRules(rules);
    }

    /**
     * 库存服务限流规则
     */
    private void initStockFlowRules() {
        List<FlowRule> rules = new ArrayList<>();

        // 线程数限流,保护数据库
        FlowRule rule = new FlowRule();
        rule.setResource("stockService:decreaseStock");
        rule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
        rule.setCount(100); // 最多100个线程同时操作
        rules.add(rule);

        FlowRuleManager.loadRules(rules);
    }
}

核心接口实现:

@RestController
@RequestMapping("/api/seckill")
public class SeckillController {

    @Autowired
    private SeckillService seckillService;

    /**
     * 秒杀接口
     */
    @PostMapping("/doSeckill")
    @SentinelResource(
        value = "seckill:doSeckill",
        blockHandler = "handleBlock",
        fallback = "handleFallback"
    )
    public Result doSeckill(@RequestParam Long userId,
                           @RequestParam Long goodsId) {
        try {
            boolean success = seckillService.doSeckill(userId, goodsId);
            return success ? Result.success("秒杀成功") : Result.error("秒杀失败");
        } catch (Exception e) {
            log.error("秒杀异常", e);
            return Result.error("系统繁忙,请稍后重试");
        }
    }

    /**
     * 限流处理
     */
    public Result handleBlock(Long userId, Long goodsId, BlockException e) {
        if (e instanceof FlowException) {
            return Result.error("当前请求过多,请稍后再试");
        } else if (e instanceof DegradeException) {
            return Result.error("服务暂时不可用,请稍后再试");
        }
        return Result.error("系统繁忙");
    }

    /**
     * 异常处理
     */
    public Result handleFallback(Long userId, Long goodsId, Throwable e) {
        log.error("秒杀异常: userId={}, goodsId={}", userId, goodsId, e);
        return Result.error("系统异常,请稍后重试");
    }
}

@Service
public class SeckillServiceImpl implements SeckillService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private StockService stockService;

    @Autowired
    private OrderService orderService;

    @Override
    @SentinelResource(
        value = "seckill:doSeckill:inner",
        blockHandler = "handleBlock"
    )
    public boolean doSeckill(Long userId, Long goodsId) {
        // 1. 校验用户是否重复购买
        String key = "seckill:user:" + userId + ":goods:" + goodsId;
        Boolean isBought = redisTemplate.hasKey(key);
        if (Boolean.TRUE.equals(isBought)) {
            throw new BusinessException("您已经参与过此秒杀活动");
        }

        // 2. 预扣减库存
        Long stock = redisTemplate.opsForValue().decrement("seckill:stock:" + goodsId);
        if (stock == null || stock < 0) {
            redisTemplate.opsForValue().increment("seckill:stock:" + goodsId);
            throw new BusinessException("库存不足");
        }

        // 3. 创建订单(可能触发熔断)
        try {
            Order order = orderService.createOrder(userId, goodsId);
            // 标记用户已购买
            redisTemplate.opsForValue().set(key, "1", 24, TimeUnit.HOURS);
            return true;
        } catch (DegradeException e) {
            // 订单服务熔断,回滚库存
            redisTemplate.opsForValue().increment("seckill:stock:" + goodsId);
            return false;
        }
    }

    public boolean handleBlock(Long userId, Long goodsId, BlockException e) {
        log.warn("秒杀请求被限流: userId={}, goodsId={}", userId, goodsId);
        return false;
    }
}

监控效果:

  • 接口QPS被控制在10000左右
  • 库存服务线程数不超过100
  • 订单服务异常时自动熔断,不影响其他服务
  • 系统整体可用性达到99.95%

6.2 第三方API调用保护

调用外部接口时,使用Sentinel进行保护:

@Service
public class ThirdPartyServiceImpl {

    /**
     * 调用第三方支付接口
     */
    @SentinelResource(
        value = "thirdParty:payment",
        blockHandler = "handleBlock",
        fallback = "handleFallback",
        fallbackClass = ThirdPartyFallback.class
    )
    public PaymentResult payment(PaymentRequest request) {
        try {
            // 调用第三方支付接口
            return thirdPartyClient.payment(request);
        } catch (Exception e) {
            throw new ThirdPartyException("支付接口调用失败", e);
        }
    }

    public PaymentResult handleBlock(PaymentRequest request, BlockException e) {
        // 返回默认结果,后续异步重试
        return PaymentResult.pending();
    }
}

/**
 * 降级处理类
 */
public class ThirdPartyFallback {

    public static PaymentResult handleFallback(PaymentRequest request, Throwable e) {
        log.error("支付接口异常,使用降级逻辑", e);
        // 返回降级结果,走人工处理流程
        return PaymentResult.manual();
    }
}

@Configuration
public class ThirdPartySentinelConfig {

    @PostConstruct
    public void initRules() {
        // 慢调用熔断
        DegradeRule slowRule = new DegradeRule();
        slowRule.setResource("thirdParty:payment");
        slowRule.setGrade(RuleConstant.DEGRADE_GRADE_SLOW_REQUEST_RATIO);
        slowRule.setCount(1000); // RT > 1秒
        slowRule.setSlowRatioThreshold(0.5); // 50%慢调用
        slowRule.setTimeWindow(30); // 熔断30秒
        slowRule.setMinRequestAmount(5);

        // 异常比例熔断
        DegradeRule exceptionRule = new DegradeRule();
        exceptionRule.setResource("thirdParty:payment");
        exceptionRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        exceptionRule.setCount(0.3); // 30%异常
        exceptionRule.setTimeWindow(30);
        exceptionRule.setMinRequestAmount(5);

        DegradeRuleManager.loadRules(Arrays.asList(slowRule, exceptionRule));
    }
}

6.3 系统自适应保护

Sentinel提供了系统自适应保护规则,根据系统的负载情况自动调整:

@Configuration
public class SystemProtectConfig {

    @PostConstruct
    public void initSystemRules() {
        List<SystemRule> rules = new ArrayList<>();

        // CPU使用率保护
        SystemRule cpuRule = new SystemRule();
        cpuRule.setHighestSystemLoad(0.8); // CPU使用率超过80%时触发
        rules.add(cpuRule);

        // 平均RT保护
        SystemRule rtRule = new SystemRule();
        rtRule.setAvgRt(1000); // 平均RT超过1000ms时触发
        rules.add(rtRule);

        // 并发线程数保护
        SystemRule threadRule = new SystemRule();
        threadRule.setMaxThread(500); // 并发线程超过500时触发
        rules.add(threadRule);

        // 入口QPS保护
        SystemRule qpsRule = new SystemRule();
        qpsRule.setQps(10000); // QPS超过10000时触发
        rules.add(qpsRule);

        SystemRuleManager.loadRules(rules);
    }
}

七、Sentinel Dashboard使用

7.1 Dashboard部署

# 下载Dashboard
wget https://github.com/alibaba/Sentinel/releases/download/1.8.6/sentinel-dashboard-1.8.6.jar

# 启动Dashboard
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 \
     -Dproject.name=sentinel-dashboard \
     -jar sentinel-dashboard-1.8.6.jar

7.2 客户端配置

# application.yml
spring:
  application:
    name: sentinel-demo
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8719
      eager: true
      datasource:
        flow:
          type: file
          file: classpath:sentinel-flow.json
        degrade:
          type: file
          file: classpath:sentinel-degrade.json

7.3 Dashboard功能

Dashboard提供了以下功能:

  • 实时监控:查看QPS、RT、成功率等指标
  • 规则配置:动态配置流控规则、熔断规则等
  • 集群流控:配置集群限流
  • 机器列表:查看连接到Dashboard的所有机器

八、总结

Sentinel作为一款成熟的流量控制组件,在微服务架构中发挥着重要作用:

  1. 核心价值

    • 流量整形:平滑处理突发流量
    • 服务保障:保护核心服务稳定
    • 实时监控:全方位流量监控
    • 快速失败:避免资源耗尽
  2. 适用场景

    • 秒杀/抢购场景
    • 第三方API调用
    • 微服务链路保护
    • 系统自适应保护

通过合理使用Sentinel,可以有效提升系统的稳定性和可用性,让微服务架构更加健壮。