十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

高并发下的弹性扩容实战:提报系统从8核到50核的云端架构演进

高并发下的弹性扩容实战:提报系统从8核到50核的云端架构演进 每年大促前一个月我们团队都会进入一种又爱又恨的备战状态爱的是流量涨上去之后各种性能问题终于有动力彻底解决恨的是每次大促都能在提报环节暴露出几个平时根本测不出来的隐性问题。今年这套淘宝活动提报系统经历了从日常8核到峰值50核的弹性扩容同时引入了云端分布式架构和多IP段调度算是把大促这块硬骨头彻底啃下来了。这篇文章不聊虚的就把我们怎么设计、怎么扩容、踩了哪些坑、最后怎么稳定扛住峰值的过程完整拆开讲。提报系统虽然不像交易链路那样直接涉及资金但它有一个非常恶心的特点流量不是缓慢爬坡而是活动开启瞬间的尖峰脉冲。零点一过商家端、平台端、外部渠道会同时涌进来持续几分钟的高并发写入后迅速回落。这种流量模型对系统的弹性能力和架构设计提出了完全不同的要求也决定了我们不能按传统方式去堆机器。文章会从流量模型分析、云端分布式架构拆分、多IP段的实际作用、50核弹性扩容操作细节、以及大促当天的兜底策略这几个维度展开适合正在做电商大促系统、活动运营平台或者任何涉及短时高并发场景的开发和运维同学参考。1. 大促提报系统的流量画像为什么日常8核会在大促前夜被打穿在讲架构之前必须先搞清楚一个核心问题这个系统到底扛的是什么流量。很多团队一提到高并发就想着上各种中间件、做各种复杂的分库分表但如果不把流量模型分析清楚所有设计都是盲目的。1.1 提报系统的三类流量来源淘宝活动提报系统的核心职能是承接商家报名和活动数据同步。我们梳理下来流量来源主要有三类。第一类是商家端主动提报。这里包括商家在千牛后台创建提报计划、填写活动商品、上传素材、提交审核。这类请求的特点是单个请求开销大——一次提报可能要写十几张表涉及商品校验、库存校验、类目权限校验、历史记录比对等一连串逻辑单次请求的CPU消耗远高于普通读接口。第二类是平台侧任务触发。比如活动开始前的批量状态流转、库存快照回写、招商规则的定时同步。这类流量是定时炸弹往往集中在整点触发瞬间产生大量数据库操作。第三类是外部渠道回调和查询。提报系统需要跟多个渠道方对接推送活动数据、接收渠道回传的报名结果同时还要处理渠道方的批量查询请求。这三类流量叠加在一起就形成了一个非常陡峭的尖峰模型。我们用压测数据说话日常状态下整个集群的CPU使用率大概在15%到20%之间8核的配置完全够用。但活动开启前30秒开始CPU使用率会在极短时间内飙到85%以上如果活动涉及秒杀级的爆品报名瞬时QPS能冲到日常的30倍以上。1.2 为什么说按峰值扩机器是最偷懒也最烧钱的做法很多团队面对这种尖峰流量的第一反应是把机器配高一点直接一步到位扩到50核常驻。这种方案从技术上讲确实能解决问题但从成本和资源利用率的角度看非常浪费。大促和双11这种级别的活动每年只有几次50核的机器跑平时15%的负载意味着85%以上的算力在绝大多数时间都是闲置的。更关键的是提报系统的瓶颈往往不在CPU本身。我们第一年做扩容时犯过一个典型错误看到CPU飙高就无脑加核结果加到一定程度发现CPU降下来了但接口RT还是很高。一排查才发现瓶颈已经转移到数据库连接池、Redis连接数、甚至日志写入的磁盘IO上了。单纯增加CPU核数反而会因为线程数增多导致数据库连接竞争更加激烈系统整体吞吐量不升反降。1.3 流量模型决定架构方向分析到这里结论已经比较清晰了这个系统需要的是平时够用、峰值能炸的弹性能力而不是常年超配的固定资源。这也直接决定了我们后续的技术选型——必须上云端必须做分布式必须让扩容这个动作变成可自动化的操作。接下来的所有设计都是围绕这个流量模型展开的。2. 云端分布式的底座设计网络、服务与存储的取舍确定了弹性扩容的方向之后下一步就是搭建支撑弹性的分布式底座。这里的核心原则是架构要向云原生靠拢但不要为了分布式而分布式。我们最终选择的是轻量级分布式方案而不是一上来就搞微服务全家桶。2.1 服务拆分按生命周期拆而非按功能拆提报系统如果按照传统方式拆成商家服务、商品服务、审核服务、同步服务会出现一个尴尬局面一次提报请求要经过四五个服务的远程调用网络开销和序列化开销反而比单机时代更严重。我们采用的是按数据生命周期拆分的策略。核心链路拆成两个服务一个是提报写入服务负责处理商家端所有的提交和校验逻辑一个是同步分发服务负责把提报结果推送给内部审核系统和外部渠道方。查询类的逻辑单独拆出一个只读服务数据通过异步方式同步过去。这样拆分之后写入服务和查询服务的资源消耗特征完全不同可以独立设置弹性伸缩策略互不干扰。2.2 存储层选型MySQL分库分表加Redis前置缓存数据存储这块我们坚持了一条原则核心数据必须落在关系型数据库上缓存只能加速不能承诺。提报数据本身有强一致性的要求比如同一个商品不能重复提报、活动库存不能超卖这些只能靠数据库的事务和唯一索引来保证。最终方案是MySQL做分库分表按照商家ID做哈希拆分分成4个库16张表。这个分片规模在50核的扩容压力下完全够用而且不需要引入复杂的中间件用ShardingSphere的轻量模式就能搞定。Redis作为前置缓存主要缓存三类数据活动基本信息、商品可提报状态、商家维度的限流计数。这三类数据的共同点是读多写少、变更频率低非常适合放在缓存里。2.3 与云平台能力的配合基础组件能托管的就不自建在搭建底座时我们做了一个很重要的决策消息队列、注册中心、配置中心这些基础组件全部使用云平台提供的托管服务而不是自己搭建集群。原因很简单这些组件虽然开源方案很成熟但自建意味着要面对版本升级、高可用部署、监控告警、故障恢复等一系列运维成本。在大促备战期间时间和精力应该花在业务链路的优化上而不是去维护一套Kafka集群。托管服务虽然没有自建那么灵活但胜在稳定和省心。比如云上的消息队列自带监控和消息轨迹出了问题能快速定位是哪条链路消费慢了配置中心支持灰度发布和秒级生效大促期间调整开关不需要重启应用。这些能力如果自己搭尤其是要在大促前完成投入产出比非常低。2.4 网络规划一台机器扛不了的事交给内网LB和可用区云端分布式还有一个容易被忽略的点网络规划。我们一开始只把应用做成了多节点入口还是单点暴露结果某个可用区的网络抖动直接导致了全站不可用。后来调整成多可用区部署入口挂两个负载均衡实例做冗余后端应用均匀分布在不同可用区任何一个可用区挂掉流量都能自动切到另外一边。存储这块我们也做了相应的主从跨可用区部署主库在可用区A从库在可用区B半同步复制保证数据不丢。虽然跨可用区的网络延迟比同机房高一两毫秒但对提报这个场景来说完全可接受换来的却是实实在在的容灾能力。3. 多IP段在提报链路中的四个真实用途多IP段这个概念在很多项目里被当成一种黑科技来用但实际场景中它解决的都是非常具体的问题。在这里要先把边界说清楚我们讨论的是在云端分布式部署中因高可用、负载均衡、出网容灾等业务需要而采用多IP段调度这是常规的架构设计手段。3.1 入口流量的多地域多IP接入提报系统的入口流量来自商家端而商家可能分布在全国各地。如果所有请求都集中到一个地域的IP段一方面跨地域的网络延迟会直接影响写入接口的RT另一方面单地域入口在流量瞬时爆发时容易成为瓶颈。我们采用的方式是在两个核心地域分别部署接入层各自绑定独立的公网IP段通过智能DNS解析把不同地域的商家请求调度到就近的接入点。这样既缩短了物理链路也将入口压力分摊到两套独立的网络环境中。某地域网络抖动时智能DNS会在分钟级内完成摘除和切换。3.2 出网调度的多IP轮换提报系统有一个非常高频的动作向外部渠道方推送活动报名数据。这里的出网请求有一个天然问题——大量请求集中在同一出口IP时很容易被对方的安全策略误判为异常流量触发限流甚至封禁。虽然我们的请求都是合规业务数据但对方的频率控制策略可不管这么多只看到同一个IP在短时间内发起了大量请求。为了解决这类对接问题我们在出网网关层配置了多个公网IP按照轮询策略分发连接。每个渠道方的回调请求都会被均衡到不同的出网IP上单IP的请求频率显著下降整个对接过程顺畅了很多。这一点对于任何需要跟外部系统高频交互的平台型业务都有参考价值。3.3 内部服务间调用的IP隔离分布式架构下服务间的调用非常频繁。我们把同一套服务的多台机器分散在两个IP段里配合注册中心的标签路由功能实现了流量的精细化调度。比如大促压测时可以把压测流量打到特定IP段的机器组上不影响正常业务流量发布新版本时也可以先发布其中一个IP段的机器观察无误后再全量发布。这种按IP段做隔离的方式比单纯按机器名做白名单要灵活得多。因为IP段天然对应着网络拓扑的位置跟可用区、机架信息天然绑定运维侧的理解成本也很低。3.4 跨可用区的故障域设计多IP段本质上是多云网络架构的自然结果。当应用分布在多个可用区时每个可用区的机器天然处于不同的IP段。从故障域的角度来看单可用区内的网络设备故障、光纤被挖断这类小概率事件只能影响该可用区对应的IP段其他IP段不受牵连。我们的大促策略是所有核心服务的副本数必须均匀分布在至少两个可用区任意一个可用区的IP段被整体摘除后剩余节点仍能承担全部流量。这个设计在模拟故障演练中反复验证过确保的不是不失败而是失败后能自动恢复。4. 弹性扩容到50核容量计算、伸缩策略与实战操作底座搭好之后最核心的问题来了大促高峰期怎么从日常的8核弹性扩到50核以及在什么时机扩、扩到什么程度、怎么保证扩容过程中不发生抖动。4.1 容量计算50核这个数字是怎么来的先看一组我们压测得到的数据基于8核规格的单节点从单节点能力反推集群规模。8核实例经过充分优化后单机QPS大概在1100左右我们按单机只能跑到峰值的70%来预留缓冲也就是一台8核实例在压力最高时承载770 QPS。50核如果是按5台10核的规格来分配总承载能力约4800 QPS覆盖预估的4500峰值QPS还能有约7%的余量。这是50核这个配置的由来。这里需要强调的一点是CPU核数只是容量规划的一个维度连接数、带宽、存储IO都要同步评估。我们在压测中发现当单机QPS超过900之后数据库连接池会先于CPU出现饱和。因此容量规划时还同步调整了连接池参数每台实例的最大连接数从40提到60数据库侧的最大连接数同步从200提到350确保扩容后数据库连接不会成为新的瓶颈。4.2 弹性伸缩的策略设计确定容量之后接下来要解决的是什么时候扩、什么时候缩。我们采用了定时扩容指标触发扩容双轨制。定时扩容针对的是已知的流量高峰。大促活动的开启时间是确定的我们会在活动开始前30分钟创建扩容计划把集群规格从8核提升到30核。预留30分钟是为了给实例启动、JIT预热、缓存预热留足时间。如果等流量真上来了再扩容从创建实例到加入负载均衡再到服务可用整个流程最快也要3到5分钟这期间进来的流量早就把现有节点打垮了。指标触发的扩容针对的是未知的流量突发。我们设了三层告警与动作CPU使用率连续2分钟超过70%时自动追加扩容节点超过80%时触发紧急扩容同时通知值班人员确认是否需要提前启动下一轮活动超过90%时直接把扩容步长加大一倍以最快速度增加节点。4.3 扩容操作的几个关键动作纸上谈兵没有意义我给你看一下实际扩容时的操作路径。我们用的是云平台的弹性伸缩组核心配置大致如下扩容动作本身是云平台负责的真正考验人的是把新节点接入现有系统的过程。我们的初始化脚本里做了四件事拉取最新配置与依赖、把新节点的IP注册到负载均衡与注册中心、预加载热点活动数据到本地缓存、执行一次轻量自检并上报健康状态。这套初始化的时间从裸机到正式接流控制在90秒以内。4.4 扩容过程中踩过的两个坑第一个坑是缓存雪崩。有一次压测时我们同时扩了5台新节点这些节点启动后都处于缓存空载状态。流量切进来的一瞬间所有请求全部穿透到数据库数据库连接瞬间被打满旧节点也跟着一起遭殃。后来我们在初始化脚本里加了一步缓存预热新节点启动后先从数据库把热点活动、热卖商品这些key加载到本地缓存同时把Redis连接池预热到最小水位。这一步看起来不起眼但直接决定了扩容动作是平滑扩容还是自爆扩容。第二个坑是负载均衡的连接耗尽。云上的负载均衡默认对新加入的节点有一个缓慢增加权重的过程这个机制本意是好的但如果后端节点在短时间内大量加入负载均衡的并发连接数会迅速打满反而把新节点挡在外面。我们的解决方法是分批扩容每批最多扩两台等前一批节点的CPU稳定在合理区间后再扩下一批。虽然总耗时变长了一些但整个过程没有任何抖动。5. 限流、降级与数据一致性扩容解决不了的三个问题扩容能解决算力不足的问题但解决不了一切问题。即使扩到了50核如果大促期间全部流量都毫无节制地打到数据库上系统该垮还是得垮。这一章讲的是另外三道保险限流、降级和一致性保障。5.1 分层次的限流策略我们在三个层级做了限流每一层的职责都不一样。接入层限流按商家维度做QPS限制。每个商家每秒最多提交多少条提报请求超出的直接返回系统繁忙请稍后重试。这个限流是基于Redis的令牌桶实现的key是商家ID桶容量和恢复速率可以根据活动级别动态调整。应用层限流按接口维度做比如一次活动只能续报3次、商品状态流转接口每秒只能执行N次。应用层限流用Sentinel实现配置了冷启动模式避免大促开始时流量瞬间达到峰值导致限流器误杀。数据库层我们用了连接数限制加上慢查询自动熔断机制当某个SQL的执行时间超过阈值且频率很高时自动降级为返回缓存数据或者直接拒绝不再让慢SQL占用宝贵的数据库连接。5.2 大促当天的三次降级预案降级我们提前写好了预案分成三级每一级都明确在什么条件下触发、触发后对业务的影响是什么。一级降级是关闭非核心功能。提报系统里有一个相似活动推荐的接口平时能提高商家的使用体验但大促期间它对数据库有额外压力。这一级降级会直接关闭该接口前端隐藏对应模块对核心提报链路的影响为零。二级降级是异步化。提交审核环节原本是同步调用商家提交后必须等到审核状态返回才算成功。二级降级会把审核动作改成消息异步处理提交接口只要把数据写入本地表、发一条消息出去就直接返回已接收审核结果通过回调或商家主动查询来获取。这能极大缩短接口RT同时削掉审核链路的同步压力。三级降级是缓存兜底。如果数据库出现严重性能问题活动详情的读取会直接切到Redis缓存即使数据不是严格实时也能保证页面可打开、流程可继续。这属于最后一道防线大促期间我们做过压测三级降级全部触发后系统的可用性依然能维持在99.9%以上。5.3 提报数据的最终一致性保证异步化和降级带来的最大挑战是数据一致性。比如商家提交了提报但审核结果还没返回这时候商家端看到的状态和数据库里的实际状态是不一致的。我们采用了对账补偿机制每天定时扫描提报表中状态为处理中的过期数据重新触发或者标记失败同时每条提报记录都有一个唯一的业务流水号渠道方回调时带上流水号我们按流水号做幂等校验重复回调不会产生重复数据。这套方案在技术实现上不复杂但必须在系统设计初期就预留好流水号字段和状态机逻辑。如果从一开始就没考虑幂等后面想补非常痛苦。这也是我想提醒所有做类似系统的人的一点大促方案可以后补但数据结构的预留必须提前。6. 大促收尾成本回缩与复盘清单大促结束后系统不能一直保持在50核状态该缩的缩、该关的关同时要把这次大促的数据和经验沉淀下来。6.1 弹性收缩的节奏控制我们的收缩策略是分步走的不是大促一结束立刻把集群降到日常规格。活动结束后的一小时内商家会集中查看报名结果、导出报表这个时段的查询流量依然很高直接缩容会导致大量查询超时。我们的节奏是结束1小时后先缩到30核观察半小时确认稳定后缩到16核再等两个小时流量完全回落后回到日常的8核。每一步缩容前都要看一眼监控大盘上的QPS、CPU、RT三个指标任何一个指标出现异常就暂停收缩。弹性伸缩组里配置了冷却时间缩容动作触发后15分钟内不会再次触发防止因指标抖动导致反复横跳。所有缩容操作同样走自动化的伸缩组策略不需要人工一台台去释放。6.2 大促成本复盘50核是不是最经济的选择这是一笔必须算清的账。按照云平台的大促优惠价格估算50核规格运行6个小时加上期间弹性追加的节点总费用相比常年固定50核的方式节省约65%。而且大促期间申请到的临时折扣比日常包年包月更划算实际支出比预算还少了近两成。但这里我想给一个可能有点反直觉的建议如果一年超过5次的大促活动都会触发扩容那不如直接在活动期间用包周的预留实例而不是每次都用按量付费的弹性实例。按量付费的优点是灵活缺点是单价高预留实例虽然要提前规划容量但大促的开启时间和峰值范围基本都是可预测的用预留实例能把单位成本再压下来两三成。6.3 一份可以直接抄的复盘清单每次大促结束我都会让团队按这份清单走一遍确保所有问题都被记录到、所有改进都落到了下个迭代里流量数据复盘实际峰值QPS、CPU水位、数据库连接水位与压测预估做对比找出差距原因。扩容记录复盘每次扩容的触发时间、执行耗时、扩容后系统稳定时间判断当前伸缩策略是否足够及时。限流与降级复盘哪些限流规则触发了误杀哪一级降级实际用到了降级后用户体验数据有什么变化。成本复盘实际支出对比预算找出可优化的资源项。告警规则复盘哪些告警在这次大促中变成了狼来了式的无效告警哪些关键告警覆盖不足。这份清单的执行效果比任何技术方案都更能决定下一次大促的稳定性。回过头来总结这次的整体操作我最深的体会是弹性扩容不是一种把按钮按下去就完事的能力它需要流量模型分析、架构预留、自动化能力、应急预案四者配合。容量计算能告诉你需要多少核多IP段设计能让你在扩容时不用顾虑网络层面的约束而限流降级才是兜住底的那张网。如果你正准备优化自己负责的活动系统建议先从流量画像和容量预估入手把这两个问题想清楚再去买机器和调参数方向就不会跑偏。
返回列表