容量规划与成本估算

容量规划的输出不是一张机器清单,而是一条“业务指标 → 资源需求”的可复核推导链,加上每个环节的安全水位。写不出公式的地方就是拍脑袋的地方。IM 特有的三笔账必须分开算:连接吃内存、消息吃带宽与分区、历史消息吃存储与成本,用同一套手段去治三类瓶颈只会两头不讨好。

从业务指标到资源需求

业务输入:DAU、人均上行/下行条数、群规模分布、图片视频占比、消息保留天数、在线率曲线
   ↓
技术指标:峰值 CCU、上行 QPS、下行 QPS(含扇出)、存储日增、出口带宽、鉴权 QPS
   ↓
单机能力:压测标定的单机连接数、单机消息 QPS、单连接内存、单机网卡带宽(见 26 章)
   ↓
资源需求:实例数 = ceil(峰值指标 / (单机能力 × 0.7)),再按 N+1 或 N+2 加冗余
   ↓
成本估算:机器 + 带宽 + 存储 + 中间件 + 推送 + 可观测,整体乘冗余系数

单机能力那一格必须来自压测的拐点而不是规格书:8C16G 的接入进程可能撑 8 万连接,也可能只撑 4 万,差别在协议开销、TLS 与写缓冲策略上。

第一笔账:连接内存

组成部分单连接典型开销说明
读缓冲1~4 KB按最大帧长设,设成 64KB 是纯浪费
写缓冲1~4 KB慢客户端出现时才会放大,必须有上限
连接对象与会话结构1~2 KB含 uid、设备、心跳时间、订阅关系
协程或线程栈4~8 KBGo 初始栈 8KB 起,按需增长
TLS 会话状态8~16 KB公网长连接基本都要开,这笔开销不能省
路由表一侧50~100 B存在 Redis 里,只算 Redis 内存
单连接内存 ≈ 读缓冲 + 写缓冲 + 连接对象 + 协程栈 + TLS 状态 ≈ 20 ~ 50 KB
堆内存     = 连接数 × 单连接内存           8 万 × 32KB ≈ 2.5 GB
容器内存   = 堆内存 × (1 + 冗余系数 0.5)     ≈ 3.8 GB → 8 万连接的接入进程按 4~6 GB 规划
Redis 路由 = 在线连接数 × 100B              300 万连接 ≈ 300 MB,用集群分区承载
  • 慢客户端是内存杀手:写缓冲必须设硬上限(如 256KB),超限直接断开并记录;否则一个只连不读的客户端能把单机内存吃掉几十 MB。
  • 断连后内存不立刻归还是 Go 的正常行为(惰性归还),规划时按峰值而非稳态估算,压测时看 heap_released 而不是 RSS。

第二笔账:带宽

上行带宽 = 上行 QPS × 单条上行字节 × (1 + 协议头占比 0.1)
下行带宽 = 上行 QPS × 平均扇出 × 单条下行字节
心跳带宽 = 连接数 × 心跳帧字节 / 心跳间隔秒数
出口峰值 = max(上行, 下行) × (1 + 重传与握手开销 0.15)
上行:5 万条/s × 300B                       ≈ 15 MB/s ≈ 120 Mbps
下行:平均扇出取 3 → 15 万条/s × 300B        ≈ 45 MB/s ≈ 360 Mbps
心跳:300 万连接 × 24B / 30s                 ≈ 2.4 MB/s ≈ 19 Mbps
建连风暴:1 万次/s 建连 × TLS 握手约 6KB      ≈ 60 MB/s ≈ 480 Mbps(持续几秒)

扇出是乘数不是加数:同一批消息发给 500 人群,下行直接放大 500 倍。容量表里必须把“平均扇出”和“最大扇出”分开列,只填平均值会在一个热点群里全线超限。心跳本身可以忽略,真正的带宽与 CPU 消耗在 TLS 握手和断线重连上(01 章已给过这个结论,压缩成本时要往这里看)。

第三笔账:存储

日增行数 = DAU × 人均上行条数 × (1 + 需要单独落库的收件箱比例)
存储字节 = 日增行数 × 单条落库字节 × (1 + 索引与冗余系数 0.3)
保留总量 = 存储字节 × 保留天数 × 副本数
项目取值说明
DAU1000 万业务给定
人均上行40 条/日业务给定
落库行数4 亿行/日群聊按读扩散只落一份,写扩散会乘群人数
单条落库字节500 B正文 + 发送者 + 会话 + 时间戳,含索引开销
日增存储约 260 GB/日4 亿 × 500B × 1.3 ≈ 260 GB,按 300 GB 预留
保留 180 天约 54 TB(单副本)三副本约 160 TB,这是成本的主要来源
离线消息近 7 天热点热数据放 Redis/SSD,冷数据归档

成本优化按收益排序:图片视频只存 URL 走对象存储与 CDN(占字节数的大头,最有效)→ 冷数据列式压缩归档(常见 3~5 倍压缩比)→ 离线队列设 TTL → 按会话活跃度分级保留(活跃会话 180 天、沉默会话 30 天)→ 历史消息归档到低成本存储并按需回捞。

资源换算与冗余

实例数 = ceil(峰值指标 / (单机能力 × 0.7))
接入层:N+2(发布与单机故障都会触发连接重建,重建本身还有风暴)
逻辑层:N+1(无状态,扩容快,冗余可以小)
路由层:按分片固定(如 8 分片 × 3 副本),不随流量线性扩,扩的是分片数
跨 AZ:每层至少 2 个可用区,单 AZ 故障时另一区要能承接 100% 流量
       即每个 AZ 都按 50% 负载部署 → 机器数实际上要接近翻倍
  • 跨 AZ 双倍部署是 IM 成本的隐形大头,但省不得:单 AZ 只留 30% 余量的方案,在真正掉一个区时会因为重连风暴彻底崩掉。
  • 定容用日曲线的 P99 峰值(跨年零点这类尖峰)而不是平均峰值,并把峰值系数写进文档,否则同一份数据会被不同的人算出十倍差距。

成本估算表模板

项目计费口径数量单价(填你所在云的公开价目表)月成本
接入层机器8C16G 包年或按量50 台(含冗余)待填
逻辑层机器4C8G20 台待填
Redis 集群内存规格512 GB待填
MySQL规格 × 分片数16 分片 × 3 副本待填
对象存储GB/月54 TB待填
公网带宽按 95 峰值计费500 Mbps待填
厂商推送按调用量按离线用户数估待填
可观测日志存储 + 采样流量按采样率估待填

单价一栏请填你自己云的公开价目表数值:不同云、不同付款方式(包年包月 vs 按量)差异可达 3 倍以上,任何写死的具体报价都会过期,只保留口径与公式才可复核。

分片与扩容预留

  • 分片数一次规划到 3 年:按 uid 哈希 16 库 × 64 表,给扩容留 4 倍空间;提前分片远比后期在线迁移便宜。
  • 连接容量与存储容量分开规划:接入层能无脑加机器,路由表与会话表的扩容需要数据迁移窗口,节奏完全不同。
  • 留一条“降级后仍能服务”的底线容量:按关闭离线推送、只保在线用户、整体限流 40% 后的量级准备资源(见 28 章)。
  • 每次容量评审后回填实际值:预估 8 万连接、实际 5.5 万,就把单机能力改成 5.5 万,下一次估算才有基准。

容量规划表模板

模块峰值指标单机能力实例数冗余日增数据保留期备注
接入层400 万 CCU8 万连接50+2无-跨 2 个可用区
逻辑层5 万条/s8000 条/s9+1无-无状态,看重下游
路由层400 万 key按分片8 分片3 副本300 MB会话期Redis 集群
消息库4 亿行/日单表 3000 万行16×643 副本260 GB/日180 天冷热分离
离线队列300 万用户按内存512 GB1 副本按 TTL 回收7 天只存位点与摘要

填完这张表,容量评审的争论点会从“感觉不够”变成“哪个数字需要修正”,这就是容量规划最大的价值。

小结:容量规划把业务输入翻译成 CCU、上行 QPS、带宽与存储日增,再用压测标定的单机能力除以 0.7 的安全水位换出实例数;三笔账分别对应连接内存(按 20~50 KB/连接估)、带宽(按字节数× 扇出,不是消息条数)与存储(日活 × 人均条数 × 单条字节 × 保留天数 × 副本数);成本大头在存储与公网带宽,优化优先级是媒体外链、冷数据压缩归档、离线数据 TTL 与分级保留,最后把结论固化进容量规划表并定期回填实际值。

笔记加载中…