晚上九点四十,城市体育场的演唱会结束,三万名观众几乎在同一时间打开手机叫车、查路线、找夜宵。如果你是这家平台的稳定性负责人,接下来十分钟会看到这样的曲线:边缘网关某台机器的流量在五分钟内涨了四倍,推荐服务某个数据分片的 QPS 冲到了日常峰值的两倍以上,而整个集群的平均 CPU 从 40% 涨到 55%——看起来一切正常,但已经有用户反馈页面转圈、下单失败。这个现象背后,是一个在容量规划里很容易被忽视的课题:超本地事件(hyper local events)的容量规划。
超本地事件指的是发生在极小地理范围内、时间窗口极短的高强度流量冲击。演唱会散场、地铁口晚高峰叠加降水、商圈新店开业、网红餐厅集中打卡,都属于这一类。它和电商大促那种全局洪峰完全不同:总量不大,但局部冲击强,而且叠加在线上流量本来就不低的时段。更麻烦的是,传统容量规划方法几乎都围绕“全局水位”设计,用这套方法去看超本地事件,结论往往是“系统没有风险”,而实际上线已经局部过热。
这篇文章想讲清楚一个判断:超本地事件容量规划的核心矛盾,不是总资源不够,而是资源在时间和空间上的错配。解决思路不是简单增加机器,而是把容量规划从“按历史高峰估总量、按集群均值看水位”,升级为“按事件预估局部峰值、按分片维度管水位、按弹性策略快速供给”。读完你会得到一套可以直接落地的容量评估、压测验证、弹性伸缩和监控告警方法,能覆盖大部分本地生活、出行、票务、外卖类业务的高频容量场景。
1. 超本地事件容量规划的核心矛盾
要理解超本地事件,先要看它和大促这类全局流量事件的区别。最直观的方式是看几个维度上的对比。
| 维度 | 全局性大促 | 超本地事件 |
|---|---|---|
| 流量总量 | 大,全网都在涨 | 相对小,集中在局部区域 |
| 空间特征 | 均匀分布 | 分片内高度集中 |
| 时间特征 | 可预期、定时开始 | 突发性强,只能短时间预判 |
| 对全局指标影响 | 明显,CPU/带宽整体拉升 | 不明显,全局均值几乎不变 |
| 主要风险 | 总容量被打穿 | 分片失衡、局部超限、热点抖动 |
| 典型业务 | 双11、618、跨年活动 | 演唱会散场、暴雨打车、商圈促销 |
从这个对比可以提炼出三个核心矛盾。
第一,全局平均掩盖局部热点。假设一个集群有 50 个节点,整体 CPU 平均 55%,看起来非常健康。但如果某个热点分片上的 5 个节点已经跑到 95%,其余 45 个节点只有 40%,平均值依然不高,用户却已经开始报错。容量规划如果只看集群平均值,超本地事件会被彻底隐藏。
第二,突发性与扩容滞后性的矛盾。超本地事件往往只提前几个小时甚至几十分钟被确认,而常规扩容链路从评估、下单、交付到拉起实例,可能需要几个小时。即使有了 Kubernetes 弹性伸缩,HPA 也存在指标采集、判断、扩容的时间差,对分钟级暴涨的响应能力有限。
第三,成本与冗余的博弈。为了应对不确定的局部事件,把所有分片都常年保持高冗余,成本不允许。但如果不预留,事件到来时又来不及扩容。这正是容量工程真正要解决的问题:在成本和稳定性之间,找到一种“按事件调配、快速伸缩、用完释放”的动态平衡。
因此,面对超本地事件,容量管理的基本单位不应该是“集群总量”,而应该是“分片水位”。后续的所有方法都围绕这件事展开。
2. 容量规划基础概念与超本地事件特征
在进入具体方法之前,先把几个基础概念讲清楚。
容量规划(Capacity Planning)不是一个专用运维术语,它描述的是:在给定负载预期下,确保系统服务能力满足 SLO 的过程。通俗说,就是回答三个问题:未来一段时间会有多少流量?现有系统扛不扛得住?扛不住时该加多少资源、加在哪里。
这里有几个常见的术语需要统一口径。
- 分片(Partition):把流量、数据或服务按一定规则拆成多个独立单元。常见有按地理位置分片、按用户 ID 哈希分片、按业务域分片。
- 单元(Cell / Unit):比分片更高一层的隔离粒度,常与地域绑定。单元化架构下,某个地域的流量主要落在对应单元内。
- 水位:一个系统或实例当前承载的负载与容量的比例。比如单实例可承载 500 QPS,当前跑了 350 QPS,水位就是 70%。
- SLO:服务等级目标,比如接口 p95 延迟小于 500ms,可用性大于 99.9%。容量规划最终要服务于 SLO。
基于这些概念,归纳超本地事件在容量层面的四个特征。
空间维度小。通常影响范围是几公里、一个商圈、一个场馆周边,而不是整个城市、整个国家。正因如此,流量会高度集中在少数几个 LBS 路由分片或单元上。即使整体 QPS 不高,对单个分片来说已经是几何级增长。
时间窗口极短。峰值可能只持续 15 到 60 分钟,来得快、去得也快。这给弹性伸缩和人工介入都带来了挑战,因为大部分自动化扩容手段的决策周期本身就在分钟级。
叠加在常态高峰之上。超本地事件不是出现在凌晨三点,而是出现在晚高峰、节假日、周末夜间这类原本就不低的流量时段。所以它消耗的不是系统的空闲容量,而是边际余量。评估时必须看“现有水位下还能承担多少额外流量”,而不是“系统能不能承受这个绝对数”。
难以依靠历史流量数据预测。历史流量曲线能看出“每晚九点全网流量上升”,但看不出“今晚体育场东侧出口的流量是西侧出口的三倍”。这需要引入事件数据源,比如票务信息、天气、活动日历、商圈运营计划,而不是只看监控曲线。
3. 传统容量规划方法为什么失效
传统容量规划通常有三条路径:基于历史峰值的趋势外推、按业务目标倒推容量、提前预留资源。这三条路径在超本地事件面前都有明显短板。
基于历史趋势外推。做法是把过去几个月到一年的流量曲线拿出来,按周期、同比、环比预测未来的峰值。这个方法适合周期性平稳的流量,比如工作日晚高峰、周末流量。但超本地事件在历史数据里往往只是一个“毛刺”,甚至因为事件时间和样本量太小,直接被平滑算法当成噪声抹掉。从历史数据里,你看不出一次暴雨会让某个城区的打车单量上涨多少倍。
按业务目标倒推。比如 DAU 要从 1000 万涨到 2000 万,根据人均请求数和资源模型,倒推出需要增加多少台机器。这是月度和季度级别的规划,对指导容量采购是有效的。但超本地事件发生在小时级,无法用业务增长模型去预测:因为没人能提前几周告诉你,下周三晚上八点某个场馆散场时会有多少人同时打开你的应用。
提前预留资源。为了应付不确定的局部热点,把每个可能发生事件的区域都分配足够的机器。这在理论上是可行的,但实操中成本极高。热点事件位置不确定、时间不确定、时长不确定,预留资源可能一周用不上一次,闲置成本全部变成透明浪费。更关键的是,如果预留资源本身不够多,或者事件规模超出预期,预留方案同样失效。
所以,传统方法失效的本质,是它的时间尺度和空间尺度都太粗。它按天、按集群规划,而超本地事件发生在分钟级、分片级。要解决这个问题,需要一套从“事件”出发的容量评估流程,而不是从“历史曲线”出发。
4. 容量预估:从事件数据推算分片峰值
超本地事件的容量评估,应该从“事件”本身开始,而不是从服务器监控开始。核心逻辑是一条链路:识别事件 — 估算参与规模 — 转化为请求量 — 分摊到分片 — 校核边际余量。
4.1 事件识别与规模估算
先回答“哪个区域、什么时间、大概多少人会受到影响”。这一步的输入不完全是技术系统,更多来自业务侧和外部数据源。
| 信号来源 | 示例 | 说明 |
|---|---|---|
| 票务平台 | 演唱会、体育赛事出票量 | 最准确的事件规模来源 |
| 天气数据 | 暴雨、大风、极端降温 | 即时配送和打车需求会突变 |
| 商业活动日历 | 商圈周年庆、快闪店、夜市 | 由运营或商户侧提供 |
| 交通事件 | 地铁故障、临时封路、大型活动散场 | 会导致周边打车、导航请求集中 |
| 社交热点 | 网红餐厅、短视频打卡地 | 突发性最强,难提前建模 |
事件识别后,要估算“会影响多少线上用户”。这里需要区分“线下参与人数”和“线上活跃人数”,因为不是每个在场的人都会打开你的应用。建议用历史同类事件的数据来校准活跃比例,不要凭经验拍脑袋。
4.2 流量估算模型
工程上比较实用的一种估算方式是分四步:
- 计算事件总请求量:参与人数 × 活跃比例 × 人均请求数。
- 计算事件平均 QPS:总请求量 ÷ 峰值持续秒数。
- 计算事件峰值 QPS:平均 QPS × 集中系数。集中系数表示流量在峰值窗口内的聚集程度,通常取 1.5 到 3。
- 分摊到热点分片:事件峰值 QPS ÷ 分片数量 × 偏斜系数。因为按地理位置路由时,事件流量很可能集中在相邻的一两个分片。
然后与当前分片的可用余量做对比:
可用余量 = 单实例压测容量 × 分片实例数 × 安全水位系数 − 分片日常基线 QPS
如果事件分摊到热点分片的 QPS 大于可用余量,就需要扩容、限流或降级。
4.3 一个容量预估的 Python 示例
下面用一个最小脚本把上面这套逻辑跑通。参数都是示例数据,核心是理解计算过程,实际值需要根据你业务压测结果来填。
# 文件路径:capacity_calc.py import math def estimate_event_qps(users, active_ratio, requests_per_user, peak_seconds, concentration): """根据事件参数估算整体峰值 QPS""" total_requests = users * active_ratio * requests_per_user avg_qps = total_requests / peak_seconds return avg_qps * concentration def balance_to_partition(event_qps, partitions, skew_factor): """将事件流量分摊到热点分片""" return event_qps / partitions * skew_factor def available_headroom(instance_capacity, replicas, safety_ratio, baseline_qps): """计算分片当前可用余量""" total_capacity = instance_capacity * replicas * safety_ratio return total_capacity - baseline_qps def main(): # 场景:3万人演唱会散场,峰值窗口约10分钟 event_qps = estimate_event_qps( users=30000, active_ratio=0.6, requests_per_user=20, peak_seconds=600, concentration=2.5, ) print(f"事件整体峰值 QPS ≈ {event_qps:.0f}") # 按位置路由,流量主要落在5个分片中的1-2个,偏斜系数取3.5 hotspot_qps = balance_to_partition( event_qps=event_qps, partitions=5, skew_factor=3.5, ) print(f"热点分片承接 QPS ≈ {hotspot_qps:.0f}") # 当前分片3个实例,单实例压测容量500 QPS,安全水位70%,日常基线400 QPS headroom = available_headroom( instance_capacity=500, replicas=3, safety_ratio=0.7, baseline_qps=400, ) print(f"热点分片可用余量 ≈ {headroom:.0f} QPS") if hotspot_qps > headroom: print("结论:容量不足,需要扩容或降级") else: print("结论:容量可以覆盖,建议继续观察") if __name__ == "__main__": main()运行:
python3 capacity_calc.py预期输出:
事件整体峰值 QPS ≈ 1500 热点分片承接 QPS ≈ 1050 热点分片可用余量 ≈ 650 结论:容量不足,需要扩容或降级这个结果非常有代表性:事件整体峰值只有 1500 QPS,在大型业务体系里不算高,但分摊到热点分片后需要 1050 QPS,分片只剩 650 QPS 余量,直接影响就是用户开始超时。按单实例容量 500 QPS、安全水位 70% 计算,至少需要新增 2 个实例才能把热点分片的水位压回安全线。
这里要特别强调一个观念:容量评估关注的是“边际容量”,也就是事件发生时系统还剩多少余量,而不是系统理论上限。“剩余容量”才是事件到来时真正能用的部分。日常水位越高,事件冲击造成的影响就越大。
4.4 容量评估的注意事项
这套计算模型的价值在于提供一个统一的讨论框架,但实际使用时有几个容易出错的地方。
集中系数和偏斜系数不要填太低。容量评审要用偏保守的取值,宁可多评估一点,也别让线上在最后一刻失守。
人均请求数要包含所有受影响接口。用户打开应用,可能同时触发首页推荐、门店详情、排队取号、下单等多个接口,如果只算主链路,会明显低估总请求量。
如果系统有缓存兜底或降级开关,需要估算降级后的请求量会有多大变化。比如降级了推荐列表,但用户会反复刷新,请求量可能不降反升,这一点要在预案中考虑清楚。
5. 压测验证:构造热点流量而不是平均流量
容量预估是纸上推演,压测是验证推演。超本地事件的压测和传统压测有一个重要区别:不能只看平均流量,必须模拟“局部集中”的流量模型。
5.1 压测环境准备
本文以 k6 压测工具为例。k6 轻量、脚本化程度高,适合构造复杂流量模型。你可以用以下任一方式安装:
# macOS brew install k6 # Ubuntu/Debian sudo gpg -k sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E34171A3B8B9B5F5D0D0D0 # 或用 Docker docker run --rm -i grafana/k6 run - <script.js需要注意的是,k6 版本更新较快,具体安装命令以官方文档为准。本文不绑定特定版本,重点在压测思路。
5.2 热点流量压测脚本
核心思路是:围绕一个热点中心坐标生成随机请求,让流量按 LBS 路由规则落到目标分片,从而模拟真实超本地事件。
// 文件路径:hotspot_stress.js // 模拟某场馆散场时,周边1.5公里内的用户集中请求 import http from 'k6/http'; import { check } from 'k6'; // 热点中心坐标,以通用经纬度为例,实际替换为目标场馆 const HOTSPOT_CENTER = { lat: 31.2304, lng: 121.4737 }; // 偏移半径,单位公里 const OFFSET_KM = 1.5; export const options = { // 先小规模验证,再逐步加压 vus: 500, duration: '5m', thresholds: { http_req_failed: ['rate<0.01'], http_req_duration: ['p(95)<800'], }, }; function randomOffset() { // 将公里数近似转换为经纬度偏移 const latOffset = (Math.random() - 0.5) * (OFFSET_KM / 111); const lngOffset = (Math.random() - 0.5) * (OFFSET_KM / (111 * Math.cos(HOTSPOT_CENTER.lat * Math.PI / 180))); return { lat: HOTSPOT_CENTER.lat + latOffset, lng: HOTSPOT_CENTER.lng + lngOffset, }; } export default function () { const pos = randomOffset(); const query = `lat=${pos.lat.toFixed(6)}&lng=${pos.lng.toFixed(6)}&scene=concert_dismissal`; const res = http.get(`http://gateway.example.com/api/nearby/recommend?${query}`); check(res, { 'status is 200': (r) => r.status === 200, }); }这段脚本的关键点有三个。
坐标围绕热点中心生成,不是随机打满全网。这样压测流量会真实触发 LBS 路由,集中打到目标分片,而不是均匀撒到整个集群。这是“超本地事件压测”和普通全链路压测的最大区别。
scene=concert_dismissal参数可以用于网关层区分流量来源,在监控和限流时做精细控制。实际是否使用取决于你网关是否支持按 query 或 header 分流。
阈值设置与业务 SLO 对应。这里的失败率小于 1%、p95 小于 800ms 都是示例,压测前应根据业务目标定好,不要等到压测中再拍脑袋。
运行压测时建议从低并发开始,逐步升高,同时盯着目标分片的实时水位。完整命令如下:
k6 run --vus 500 --duration 5m hotspot_stress.js如果 500 并发没有压力,再调整--vus到 1000、2000。如果直接满压,一旦容量预估偏差过大,可能把线上真实流量一起拖垮。
5.3 压测结果如何判断
压测不是跑完看一个通过率就结束,至少要观察以下信息:
- 目标分片的 CPU、内存、GC 是否到达上限。
- p95 和 p99 延迟是否出现拐点。延迟突然上升,往往意味着某个资源已经耗尽。
- 错误率上升前,系统是否触发了限流、降级或熔断。如果保护机制先于容量打满,说明容量预估太乐观。
- 压测结束后,分片水位能否快速回落,回收临时扩容资源是否正常。
通过压测,可以沉淀一份“单实例容量基线”,比如“标准 4C8G 下,nearby-service 单实例可承载 500 QPS,且 p95 小于 800ms”。这份基线之后会成为容量预估脚本和 HPA 阈值的重要输入。
6. 弹性伸缩与热点隔离设计
预估出了容量缺口,压测验证了缺口,接下来要解决“资源怎么补”的问题。针对超本地事件,推荐组合使用三类策略:计划内扩容、实时弹性伸缩、热点隔离。
6.1 计划内扩容:事件日历驱动
超本地事件如果能提前预判,就尽量不用实时弹性兜底。像演唱会、体育赛事、商圈活动通常有票务数据和活动日历,可以提前几小时甚至一天确认。
一个稳妥的做法是:在事件开始前 30 分钟,提升目标服务 HPA 的最小副本数,让副本先拉起来,事件结束后再缩回。这比直接修改 Deployment 副本数更安全,因为不会绕过 HPA 的管理逻辑。
# 事件开始前:将 minReplicas 临时提高到 12 kubectl patch hpa nearby-service-hpa -n production \ --type=merge -p '{"spec":{"minReplicas":12}}' # 事件结束后:将 minReplicas 恢复为 5 kubectl patch hpa nearby-service-hpa -n production \ --type=merge -p '{"spec":{"minReplicas":5}}'这里的核心思路是:提前把水位压到安全区间,实时弹性只负责兜底,而不是承担全部容量供给责任。
6.2 实时弹性伸缩:基于 QPS 的 HPA
CPU 是一个非常滞后的扩容信号,尤其在 Java 应用中,CPU 升高往往伴随着 GC 和线程竞争,反应太慢。更推荐使用业务指标的 HPA,比如 QPS、并发数、平均响应时间。
下面是一个基于 Pod 维度 QPS 的 HPA 配置示例:
# 文件路径:nearby-hpa.yaml apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: nearby-service-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: nearby-service minReplicas: 5 maxReplicas: 30 behavior: scaleUp: # 让扩容尽快生效 stabilizationWindowSeconds: 0 policies: - type: Percent value: 200 periodSeconds: 60 scaleDown: # 缩容要稳,避免流量抖动导致副本反复收缩 stabilizationWindowSeconds: 300 policies: - type: Percent value: 20 periodSeconds: 60 metrics: - type: Pods pods: metric: name: http_requests_per_second target: type: AverageValue averageValue: 300这个配置有几个地方值得解释。
扩容的稳定窗口设为 0,意味着只要指标超过阈值就立刻走扩容策略,避免缩容策略的滞后拖慢扩容。生产环境可以根据实际监控延迟调整,不建议一开始就设很大。
扩容步长是 200%,一次最多翻倍,适合事件类流量快速上涨。缩容步长只有 20%,并且有 5 分钟稳定窗口,防止峰值过后瞬间缩掉太多副本。
自定义指标http_requests_per_second来自 Prometheus Adapter 或 KEDA,一般会通过custom.metrics.k8s.ioAPI 暴露给 HPA。没有这套基础设施时,HPA 无法识别这个指标名。
需要注意,HPA 是按工作负载维度扩容,还没有解决“热点集中在某个分片”的问题。如果热点压垮的是某个数据库分片或 Redis 分片,HPA 扩容应用副本并不能直接缓解,需要配合热点隔离。
6.3 热点隔离:把热点流量和普通流量分开
超本地事件影响最严重时,通常不是应用层 CPU 打满,而是某个数据分片的热点 key 被集中访问。比如同一个商圈的门店数据、同一个场馆的推荐结果被大量请求。此时所有流量都指向少量 key,单个分片的缓存、数据库连接都会成为瓶颈。
工程上常用的手段包括几个层面:
- 服务隔离:把热点场景的接口独立部署,单独设置 HPA 和限流阈值,避免热点接口占满线程池后拖垮普通接口。
- 缓存兜底:对热点 key 使用本地缓存,减少打到底层存储的请求。本地缓存的成本远低于扩容一整个数据库集群。
- 多级限流:在网关层和应用层分别设置热点区域的限流阈值,超过阈值直接返回降级结果,优先保障核心交易链路。
- 连接池隔离:为热点分片单独配置数据源连接池,避免一个分片的连接耗尽拖垮整个应用实例。
热点隔离的配置和数据结构与业务强相关,这里不展开代码,但设计容量方案时一定要把“热点流量可能绕过应用扩容、直接冲击存储”的场景考虑进去。
7. 监控与告警:把水位管理下沉到分片
超本地事件发生时,全局监控可能一片平静,局部已经告急。因此监控设计要下沉到分片、实例级别,不能只做集群维度聚合。
7.1 关键监控指标
建议至少覆盖下面几个层面的指标。
| 监控对象 | 核心指标 | 说明 |
|---|---|---|
| 接入层 | 单边缘节点 QPS、成功率 | 热点区域的流量最先体现在接入层 |
| 应用层 | 分片 QPS、实例 CPU、RT 分位数 | 判断热点分片是否接近容量上限 |
| 数据层 | 连接池使用率、慢查询、行锁等待 | 局部热点会放大底层存储压力 |
| 依赖层 | 第三方接口 RT、失败率 | 事件流量可能集中在同一批外部依赖上 |
需要强调的是,业务埋点时要把partition_id或location_id作为标签带出来。否则监控系统只能看到“整个服务 QPS 2000”,看不到“a-03 分片 QPS 800、余量只剩 200”。
7.2 Prometheus 告警规则示例
下面两条规则分别覆盖“实例级 QPS 突刺”和“分片级错误率上升”两类典型场景。
# 文件路径:hotspot-alerts.yaml groups: - name: hotspot-alerts rules: - alert: InstanceQPSSpike expr: | sum by (pod) ( rate(http_requests_total{application="nearby-service"}[1m]) ) > 300 for: 2m labels: severity: warning annotations: summary: "服务实例 {{ $labels.pod }} QPS 超过 300" description: "当前值 {{ $value }},请检查该实例所在分片是否存在局部热点" - alert: PartitionErrorRateHigh expr: | sum(rate(http_requests_total{code=~"5.."}[1m])) by (partition_id) / sum(rate(http_requests_total[1m])) by (partition_id) > 0.03 for: 2m labels: severity: page annotations: summary: "分片 {{ $labels.partition_id }} 错误率超过 3%" description: "服务可能出现局部过载,建议检查该分片容量和限流策略"第一条规则关注单实例 QPS,适合发现“整体不高、单个实例先被打爆”的情况。第二条规则关注分片错误率,当错误率超过 3% 且持续 2 分钟时触发告警,适合小时级事件的快速感知。阈值 300 和 3% 都是示例值,应基于压测结果和 SLO 校准。
除了数值告警,还有一个很实用的可视化手段:把事件日历叠加到监控大盘的时间轴上。比如演唱会是 21:40 散场,就在大盘上画一条竖直的事件线。这样复盘时能更快确认容量预案是否生效、哪个环节出现了时间差。
8. 常见问题与排查思路
超本地事件的排查和普通故障不太一样,问题往往发生在局部而非全局,按下面这个表格排查会比较高效。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 全局 CPU 不高,部分用户报错 | 分片热点导致局部过载 | 按 partition_id 查看各分片 QPS、RT、错误率 | 为热点分片扩容或启用热点隔离集群 |
| HPA 扩容滞后 | 自定义指标采样周期过长,或扩容步长太小 | 查看 HPA 事件和指标延迟 | 降低采样周期,放宽扩容步长,配合计划内扩容 |
| 缓存命中率骤降 | 热点区域新数据写入导致缓存过期集中 | 查看缓存命中率曲线和 key 分布 | 热点 key 延长过期时间,或引入本地缓存 |
| 数据库连接池打满 | 局部 QPS 上升导致连接被长期占用 | 查看连接池监控和慢查询日志 | 提升连接池上限,拆分热点库,部分请求异步化 |
| 下游第三方接口超时 | 本地事件导致同一区域第三方请求集中 | 查看第三方调用耗时和重试日志 | 设置快速失败,熔断降级,异步聚合 |
| 事件结束后资源回收慢 | 缩容稳定窗口过长或缩容步长过小 | 查看 HPA behavior 配置和副本变化记录 | 调整缩容策略,或手动下压 minReplicas |
生产环境遇到告警时,第一件事不是去改 HPA 阈值,而是先确认“热点到底在哪个分片、当前分片还剩多少余量”。这个问题不看清,改任何全局参数都可能无效。
9. 最佳实践与工程建议
超本地事件容量规划不是一个一次性动作,而是一套持续运转的机制。从工程落地角度看,有六条建议值得沉淀为团队规范。
第一,把容量基线做成资产。每次压测后,记录服务版本、实例规格、单实例容量、安全水位、SLO 这些数据。版本升级后重新压测,把基线更新掉。没有基线的容量评估就是拍脑袋。
第二,把事件日历纳入容量评审流程。凡是大型活动、票务场次、商圈活动上线,都要像发布变更一样走一次容量评审。评审内容包括参与规模、影响半径、流量估算、弹性预案和降级开关,形成一个标准的 checklist。
第三,监控和告警必须到分片维度。如果业务是按位置路由的,那partition_id就必须出现在埋点指标里。告警规则宁可先收窄到一两个关键分片,也不要只看集群聚合数据。
第四,扩容预案要能快速回滚。计划内扩容要提前执行,事件结束后要及时缩容,否则成本会悄悄失控。每次活动结束后,应该有一个“资源回收检查清单”,确认临时副本、临时 HPA 配置都已经恢复原样。
第五,削峰填谷比硬扛更可靠。对可异步化的操作,比如推送通知、状态更新,尽量走队列削峰。对实时性要求不高的查询,可以接受短暂排队。硬扛峰值流量,本质上是在和物理资源的上限作斗争。
第六,超本地事件要有降级预案。容量永远不可能无限支撑。容量评估后如果发现无论如何都覆盖不了峰值,就要明确“保哪个接口、降哪个功能”。比如保下单和支付,降推荐和个性化内容。降级规则要提前配置、提前演练,不要在事件发生时临时写。
10. 总结与后续学习方向
回到文章开头那个演唱会散场的场景。全局 CPU 55% 不代表安全,真正决定系统能否扛住事件的是热点分片的水位和事件到来前的边际余量。容量规划不需要把每个分片都加到冗余状态,而是需要一套“事件识别 — 流量估算 — 分片校核 — 压测验证 — 弹性供给 — 监控复盘”的完整循环。
对正在做本地生活、出行、票务、外卖类业务的团队,下一步建议先做两件事:一是把事件日历和容量评审流程打通,让每一次活动上线都默认带容量评估;二是把监控指标下沉到分片维度,保证你至少能随时回答“这个分片还剩多少余量”。
更深入的扩展方向包括单元化架构下的容量建模、基于 KEDA 的自定义指标弹性、热点 key 治理、流量调度与限流降级的策略编排。这些领域互相咬合,但起点都是分片视角的容量规划。下次你手头的业务再遇到“某商圈突然爆单”,不要先改自动伸缩阈值,先把分片维度的指标拉出来看一眼,答案通常都在那里。