ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

秒杀系统架构设计07:高可用架构

秒杀系统架构设计07:高可用架构 秒杀高可用限流、熔断、降级三剑客本文是「10Wqps 秒杀架构」系列的第七篇。前面的文章已经搭建了异步下单、缓存体系、库存扣减和 MQ 架构——但这些都是正常情况下的方案。本篇讨论的是出问题的时候怎么办限流防过载熔断防雪崩降级保核心。一、开篇高可用不是保证系统不出问题而是“系统出问题后仍然能对外提供服务并且不会因为局部故障导致全局崩溃”。在秒杀场景下高可用的挑战尤为严峻流量是平时的几十倍任何一个链路节点网关、Redis、MQ、MySQL的故障都可能被放大为全链路雪崩。一个经典的雪崩路径商品服务响应变慢DB 慢查询 → 订单服务调用商品服务超时 → 订单服务线程池耗尽 → 网关调用订单服务超时 → 网关线程池耗尽 → 所有请求返回 502/504 → 整个商城不可用限流、熔断、降级是防御雪崩的三道防线。它们各自有不同的触发条件和作用范围但目标一致在局部故障时优先保证核心链路的可用性。二、核心方案2.1 限流控制流量水位限流是主动防御手段——在流量超过系统容量之前就拒绝多余的请求防止系统被冲垮。在秒杀架构中限流应该在三个维度上同时生效维度一按用户限流同一用户在 60 秒内只允许发起一次秒杀请求。这既是对正常用户的保护防止手抖多次点击也是对恶意用户的屏障防止脚本高频刷单。// Sentinel 注解方式SentinelResource(valueseckill-submit,blockHandleruserRateLimitBlockHandler)publicResultsubmitSeckill(LongseckillId,LonguserId){// 秒杀逻辑}// 限流规则配置privatevoidinitUserRateLimitRule(){ListFlowRulerulesnewArrayList();FlowRulerulenewFlowRule(seckill-submit);rule.setGrade(RuleConstant.FLOW_GRADE_QPS);// 按 QPS 限流rule.setCount(0.017);// 每用户每秒约 0.017 个请求 ≈ 60 秒 1 次rule.setLimitApp(userId.toString());// 按用户 ID 限流rules.add(rule);FlowRuleManager.loadRules(rules);}publicResultuserRateLimitBlockHandler(LongseckillId,LonguserId,BlockExceptionex){returnResult.fail(429,操作太频繁请稍后再试);}维度二按商品限流单个秒杀商品的秒杀接口设置独立的 QPS 上限。例如某商品的实际库存只有 1 万件那么秒杀接口的 QPS 上限可以设为 5000——超过这个量的请求在网关层就直接拒绝无需穿透到 Redis。FlowRuleproductRulenewFlowRule(seckill-product-seckillId);productRule.setGrade(RuleConstant.FLOW_GRADE_QPS);productRule.setCount(5000);// 单商品秒杀接口 QPS 上限维度三按接口限流对整个秒杀接口所有商品合计设置总 QPS 上限。这是最后一道兜底——即使某个商品的限流规则失效总 QPS 上限也能保护下游。FlowRuletotalRulenewFlowRule(seckill-submit-total);totalRule.setGrade(RuleConstant.FLOW_GRADE_QPS);totalRule.setCount(100000);// 总 QPS 上限限流触发后的用户体验限流不是系统错误是主动保护。返回的 HTTP 429 状态码附带有意义的提示{code:429,message:活动太火爆请稍后再试,requestId:req-20250101-123456}动态阈值调整限流阈值不应硬编码。通过配置中心如 Nacos动态下发活动期间可以根据实时 QPS 和系统负载动态调整。例如发现 Redis 延迟上升自动将商品限流阈值下调 30%。2.2 熔断阻止故障扩散限流是我预期扛不住提前拒绝熔断是我已经扛不住了暂停服务止损。熔断器的三态模型┌──────────┐ │ CLOSED │ 正常状态所有请求通过 └────┬─────┘ │ 异常率 阈值如 50% ▼ ┌──────────┐ │ OPEN │ 熔断状态直接拒绝所有请求 └────┬─────┘ │ 熔断时间窗口到期如 10 秒 ▼ ┌───────────────┐ │ HALF_OPEN │ 半开状态允许少量探测请求 └────┬──────────┘ │ ┌─────────┴─────────┐ │ │ 探测请求成功 探测请求失败 │ │ ▼ ▼ CLOSED OPEN继续熔断Sentinel 熔断规则配置privatevoidinitDegradeRule(){ListDegradeRulerulesnewArrayList();// 库存服务的熔断规则DegradeRulestockRulenewDegradeRule(stock-service).setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO)// 按异常比例.setCount(0.5)// 异常比例超过 50%.setTimeWindow(10)// 熔断 10 秒后进入 HALF_OPEN.setMinRequestAmount(20)// 统计窗口最少 20 个请求.setStatIntervalMs(10000);// 统计窗口 10 秒rules.add(stockRule);// 也可以按慢调用比例熔断DegradeRuleslowRulenewDegradeRule(order-service).setGrade(RuleConstant.DEGRADE_GRADE_RT)// 按响应时间.setCount(200)// 最大 RT 200ms.setTimeWindow(10).setMinRequestAmount(10).setSlowRatioThreshold(0.5);// 慢调用比例 50% 触发熔断rules.add(slowRule);DegradeRuleManager.loadRules(rules);}熔断的触发条件选择触发条件适用场景配置示例异常比例下游服务抛异常如连接超时、拒绝连接异常比例 50%统计窗口 10s慢调用比例下游服务响应变慢但未抛异常如 DB 慢查询慢调用 RT 200ms比例 50%异常数对异常零容忍的关键接口1 分钟内异常数 5熔断后的降级行为SentinelResource(valuestock-check,fallbackstockCheckFallback,// 业务异常降级blockHandlerstockCheckBlockHandler// 熔断/限流降级)publicStockResultcheckStock(LongproductId){returnstockServiceClient.queryStock(productId);// RPC 调用库存服务}// 熔断触发时的降级逻辑publicStockResultstockCheckBlockHandler(LongproductId,BlockExceptionex){// 返回本地缓存的最新库存数据StockResultcachedcaffeineCache.getIfPresent(stock:productId);if(cached!null){cached.setFromCache(true);// 标记数据来源前端可提示库存信息可能延迟returncached;}// 连本地缓存也没有 → 返回兜底值returnStockResult.defaultResult();}注意 fallback 和 blockHandler 的区别fallback处理业务异常如库存服务返回了错误而非不可用blockHandler处理 Sentinel 触发的流控限流、熔断此时请求根本没到达服务端2.3 降级有损服务保核心降级是在系统压力过大或部分组件不可用时主动放弃非核心功能将全部资源集中到核心链路上。秒杀场景的降级策略矩阵降级项触发条件降级行为影响验证码验证码服务不可用跳过验证码纯限流兜底安全性降低积分更新积分服务超时积分异步补发事后对账补做积分到账延迟优惠券核销优惠券服务异常率 30%秒杀价直接结算优惠券事后补退可能有差价需补退订单通知通知服务不可用通知降级为用户主动查询用户不主动刷新则不知道结果推荐/猜你喜欢流量 预期值 80%关闭推荐模块释放服务资源页面变简易Redis 读缓存Redis 集群异常降级为本地 Caffeine 缓存数据可能有延迟数据库写入MySQL 主库故障降级为仅缓存预扣DB 恢复后批量同步数据一致性风险临时方案降级 vs 熔断的异同维度熔断降级触发方调用方消费者被调用方/系统全局触发条件下游服务异常异常率、慢调用系统压力大或组件不可用作用范围单个服务调用可以影响整个功能模块恢复方式自动HALF_OPEN → 探测成功 → CLOSED手动或自动压力回落典型行为快速失败直接返回错误返回简化/缓存/默认数据关系熔断是实现降级的一种手段降级是更高维度的业务决策2.4 高可用的基础设施应用层高可用无状态设计所有业务服务不存储会话状态任意实例可处理任意请求。Session 信息存储在 Redis 中服务实例重启不影响用户。负载均衡Nginx 采用权重 IP 哈希策略既做负载分散又保证同一 IP 的请求路由到同一组后端降低缓存穿透概率。弹性伸缩K8s HPA 基于 CPU/内存/QPS 指标自动扩缩容。秒杀前 30 分钟预扩容到预估峰值实例数的 1.2 倍活动结束后自动缩容。存储层高可用Redis Cluster3 主 6 从 3 哨兵跨机房部署。主节点故障时哨兵自动将从节点提升为主切换 300ms。MySQL主从架构 读写分离。主库故障时从库自动切换为主库。代理层如 ShardingSphere统一管理读写分离和故障切换。RocketMQ主从 Broker 部署同步复制。单个 Broker 故障不影响整体。多 IDC 双活DNS 智能解析 / \ IDC-A华东 IDC-B华北 Nginx 集群 Nginx 集群 Gateway 集群 Gateway 集群 业务服务集群 业务服务集群 Redis Cluster Redis Cluster跨机房同步 MySQL 主 MySQL 从跨机房复制正常时流量按地理位置就近分发。单机房故障时 DNS 将全部流量切到健康机房切换时间取决于 DNS TTL通常 1-5 分钟。2.5 监控告警体系核心监控指标:-Nginx:请求量,4xx/5xx 比例,响应时间-Gateway:限流触发次数,熔断状态,路由延迟-业务服务:QPS,RT(P99),错误率,线程池使用率-Redis:QPS,命中率,内存使用,连接数,主从延迟-RocketMQ:消息发送成功率,消费 TPS,积压量-MySQL:QPS,慢查询数,连接数,主从延迟告警阈值:-业务错误率0.1% → 钉钉/邮件告警-接口 RT P99200ms → 钉钉告警-Redis 命中率 90% → 邮件告警-MQ 积压10000 条 → 短信 电话告警-MySQL 慢查询50/min → 钉钉告警-熔断器状态 OPEN → 短信告警需要立即响应三、应急预案3.1 秒杀前 Checklist所有服务实例已扩容到预定数量缓存预热脚本执行完成并验证数据正确Sentinel 限流/熔断规则已下发并生效降级开关配置已就绪可一键触发监控大盘配置完成告警通道畅通全链路压测通过目标 QPS 的 1.2 倍值班人员已到位明确各角色职责回滚方案已就绪指上线的新代码可一键回滚3.2 活动进行中的应急响应故障现象1 分钟内行动5 分钟内行动30 分钟内行动错误率突增检查监控大盘定位故障服务触发对应服务的熔断降级修复根因逐步恢复Redis 延迟上升检查 Redis 慢查询日志调低商品限流阈值减少 Redis 压力扩容 Redis 分片MQ 积压扩容消费者实例降级非核心消费逻辑消费速度恢复后补做降级逻辑超卖告警冻结该商品秒杀对比 Redis/MySQL 库存差异修复数据 补偿方案某 IDC 故障确认 DNS 已切流排查故障 IDC 根因恢复故障 IDC 服务四、总结限流是主动防御在用户维、商品维、接口维三个层面设置 QPS 上限超过阈值的请求直接拒绝不让无效流量消耗下游资源。熔断是自动止损当某个服务异常率达到阈值时快速失败取代慢速超时阻止故障在微服务调用链中扩散。降级是有损保核心放弃非核心功能积分、通知、推荐将资源集中于核心下单链路。需要事先定义好降级策略不能临时决策。熔断和降级不是一回事熔断是调用方的保护机制针对特定依赖降级是全局的资源调度策略针对整个系统。预案比技术更重要秒杀前 checklist、活动中的响应手册、活动后的复盘机制是保障高可用的最后一公里。
返回列表