先说一次真实的翻车
我去年 11 月帮一个做家居跨境的独立站盯过一场黑五预热。运营在 20:00 发 Push,20:00:17 开始,阿里云 SLB 入方向从 120 QPS 拉到 4700 QPS,P99 从 210ms 到 1.9s,网关 5xx 从 0.2% 到 7.3%。我们第一反应是扩 Pod,HPA 从 6 个扩到 24 个,花了 90 秒,但波峰在 20:04 已经掉了。后来复盘发现,真正救命的不是扩容,而是 20:00:03 触发的限流和 20:00:11 触发的缓存降级。这段经历让我把脉冲流量单独拎出来,不再和日常增长流量混在一起看。
脉冲流量不是“大”,是“短、陡、有前摇”
脉冲流量和常态流量最大的差别不是“大”,而是“短、陡、有前摇”。常态流量你盯日环比、周同比、容量水位;脉冲流量你盯 1 秒粒度、5 秒粒度和 1 分钟粒度。比如直播间开播、Push 到达、整点秒杀、广告计划过审,这些动作前 30 到 180 秒通常有信号:Push 平台回执量上涨、CDN 回源请求变密、购物车接口 addCart 调用从 80/min 到 600/min。只看平均 QPS 会骗人,4700 QPS 如果持续 3 分钟是 282000 次请求,如果持续 17 秒只有 79900 次,容量策略完全不一样。
我的处理顺序:先可丢,再限流,再排队,再降级,最后扩容
我自己的判断顺序:先识别可丢请求,再限流,再排队,再降级,最后才是扩容。很多人顺序反了,先扩带宽、加机器,结果数据库连接池先炸。Nginx 这层我们用的是 limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s; limit_req zone=api burst=200 nodelay; limit_conn addr 20; 这个配置不是万能,rate=100r/s 对单 IP 太粗,但对防脚本刷接口有用。网关层按用户 ID 和接口维度限流:下单 3000 QPS,库存查询 8000 QPS,商品详情 20000 QPS,超过就返 429,前端排队 8 秒再轮询。注意 429 不是失败,它是保护,前提是前端要接住。
削峰不要只会用 Kafka,下游 2000 TPS 时它只是换个地方堵
削峰不要只会用 Kafka。Kafka 12 个分区、6 个消费者,单分区 8MB/s 左右,如果下游写 MySQL 只有 2000 TPS,Kafka 只会把堆积从网关挪到消息队列。我们最后把下单拆成两步:第一步 Redis Lua 扣减预占库存,key 过期 15 分钟;第二步发 Kafka 异步落订单,落库失败再补偿。Redis 开了 8GB maxmemory,allkeys-lru,但库存 key 必须单独放一个 2GB 实例,禁止 LRU 淘汰,不然大促时库存会被当冷数据清掉。这个坑我踩过一次,凌晨 1 点赔了 37 单。
降级顺序比限流参数更值钱
降级顺序比限流参数更值钱。我们的顺序是:关个性化推荐、关实时销量排名、关优惠券叠加试算、商品详情走 60 秒 CDN 缓存、购物车只读、最后才是关闭下单入口。每层降级都有开关,配置在 Apollo,灰度 5% 流量先验证 90 秒。别小看“关推荐”,它能把商品详情接口的 RT 从 780ms 压到 260ms,因为它省掉了 3 次 RPC 和 1 次 Redis 排序。脉冲流量里,一个非核心功能可能就是压死数据库的最后 8% CPU。
独立观点:脉冲流量不是容量问题,是业务优先级问题
最后说独立观点:脉冲流量不是容量问题,是业务优先级问题。你不可能用扩容接住所有脉冲,因为成本会按峰值付,但收入只按成交付。能扛住脉冲的系统,一定要能说“不”:对谁说不、对哪个接口说不、在什么时间说不、说不之后用户看到什么。我们后来把大促目标从“零 5xx”改成“下单成功率 ≥ 98.5%,非核心接口 5xx < 15%”,反而更稳。因为 100% 可用是广告词,98.5% 才是能算账的工程目标。