到了「跑得稳」,才真正开始谈架构。不是因为你终于可以展示自己知道多少种设计模式了,是因为你终于有了足够的信息来做对的选择。
一、为什么这时候才该谈架构
「能跑」和「跑得准」阶段你也在做技术决策,但那些决策的核心是「做减法」------砍功能、砍抽象、砍依赖。到了「跑得稳」,你开始做加法了。
区别在于:前面的减法靠的是直觉和纪律,现在的加法靠的是前两个阶段攒下来的真实信息。你不知道用户会不会用的时候,上消息队列是在赌。你知道核心链路每天跑几百万条、每条都不能丢的时候,上消息队列是在做对的事。同样的决策,信息量不同,性质就不同。
你手里有数据了,你知道哪里会出问题、出问题了代价有多大------这时候谈容错、谈可观测、谈灰度,不是在给想象中的系统买保险,是在给你真实见过的系统补短板。
二、该花的三大件
容错:先承认它会失败
容错最难的地方不是技术。是心态。
大部分工程师在写代码的时候,脑子里的画面是「正常情况」------数据正常进来,正常处理,正常输出。但生产环境教会你的第一课是:没有正常情况。依赖会超时,网络会抖动,磁盘会满。数据不会按你预想的格式来,下游不会在你预期的时间回响应。
容错的第一步不是重试、回滚、降级------是先承认它会失败。 你承认了每个环节都可能出问题,才会去想「出了事怎么办」。重试是「再给一次机会」,回滚是「回到出事前的状态」,降级是「先保住大部分功能」。不要每个环节三样都做------你承受不起那个复杂度。但每个环节至少有一个兜底方案。重试不了就回滚,回滚不了就降级,降级不了至少打一条日志告诉别人这里断了。
没有完美的系统,但要在系统发生故障时能控制住损失。关键不是「怎么恢复」,是「别让一个环节的失败变成了所有环节的失败」。大多数系统不是被高并发打垮的,是被一个没预料到的失败连锁反应拖垮的。
稳定性不止靠技术,也靠预案
容错告诉你「出了问题怎么恢复」。但还有一个更基础的东西:你知不知道自己在出问题的时候该做什么。 线上出问题的时候,时间是被扭曲的。每一分钟都像一个小时,每一条消息都像一声尖叫。人在这种时候会乱------你以为是代码 bug,结果只是网络抖了一下;你以为只是重启一下,结果重启了把缓存全清空了。没有预案的人会瞎操作,在压力下乱试一通,结果本来一个小故障被手动折腾成大事故。
有预案的团队正好相反。有充足的预案,在线上出现问题时可以不慌不忙,按照操作手册来:先确认是什么问题 → 执行回滚/切换 → 确认服务恢复 → 然后再去排查根因。 优先保证服务可用性,再去做长短期的解决方案。操作手册不是「出了问题去读一遍」,是在没出事的时候就写好了,放在那里,出事了谁来都能照着做。人是会紧张的,但手册不会。
预案的价值不在于「里面写了什么」,在于「出事的时候你不用想」。你照着做就行了------不敢确定是什么问题,但确定自己该做什么。
可观测:不是用来解释过去,是用来发现现在
大部分人对可观测的理解是:出问题了,去翻日志查原因。这确实用到了可观测,但不是可观测的核心。可观测的核心是:在问题还没被用户发现的时候,你先知道。
日志解释过去------告诉你「刚才发生了什么」。指标发现现在------告诉你「现在正在发生什么」。告警预见未来------告诉你「接下来可能要发生什么」:某个队列堆积在涨、某个接口延迟在爬坡,还没到报警阈值,但方向已经不太对了。告警的价值不在「出了事通知你」,在「在还没出事的时候就告诉你你可能要出事」。用户和你同时知道出了问题,那是告警没做到位。告警该做的是比用户早一步。
可观测的价值不在「出了事能查」,在「没出事就知道可能出事」。 你早上打开仪表盘,看到某个指标在慢慢变差------还没崩,还没人报,但你在变差了。这时候你出手处理,用户不知道你做了什么,但用户感受到的是「这东西一直很稳」。
灰度:不是胆小,是给自己留退路
敢直接全量切换,说明你对系统还不够了解。
灰度发布的核心逻辑不复杂:一个变更只影响一小部分用户,出问题了,影响面在小部分里;全量了,影响面在全部里。区别不在技术自信,在代价大小。灰度不是不相信自己,是不想把所有用户的信任都押在自己的「我相信没问题」上。
灰度的背后是两条更根本的原则。
第一条:留好退路,可以回滚/撤回,最小化损失。 灰度的价值不止在于「慢慢放量」,更在于「出了问题能立刻撤回」。没有哪个变更能保证零 bug------有退路,你才不会在问题发生时被锁死。撤回不是在承认你错了,是在说你知道怎么保护自己。
第二条:出现问题优先解决,而非追责或调查根因。 灰度的流程是:发现异常 → 立刻回滚 → 恢复服务 → 然后再查原因。不是先查根因再回滚------你查根因的这段时间用户还在受影响,服务还在坏着。先止损,再复盘。把影响控制在最小范围,把时间花在解决问题上,而不是花在「谁写的这个 bug」上。
三、不要为「万一」做架构
复杂度不是免费的。每一份复杂度都有维护成本------你加了重试逻辑,重试本身也可能出错,重试的次数、间隔、幂等逻辑都要维护。你加了监控,监控本身要占资源,指标采集、告警配置、阈值调优每一个都在吃你的时间。你加了灰度,灰度就要维护流量路由、用户分群、回滚方案------这些都是复杂度,不是一次性的代码,是持续性的责任。
所以「该花的」和「不该花的」之间有一条很清晰的线:看成本。
不是看「这个方案能不能做到」,是看「做这件事的成本,能不能被业务风险抵回来」。一个日活几百的内部系统,上微服务、分库分表、多活容灾------出一次故障损失几十块钱,但那套基础设施每个月的维护成本是几千块钱。
学会做减法。日志要打,但不能打崩系统------日志量上来把磁盘写满了,你加了一个「保障」结果自己成了故障源。预发环境要有,但要在成本允许的范围内------花几十万搭一套和线上 1:1 的预发,为了提前发现一个几周才出一次的问题,不值。
把复杂度和思考放在产生最大价值的地方。 不是所有环节都要容错,是最核心的链路要容错。不是所有指标都要监控,是业务最敏感的那几个要监控。不是所有变更都要灰度,是影响核心链路的变更要灰度。
四、案例:不该花的复杂度长什么样
第2篇里讲过一个 Airflow 的案例------定时任务只有两个,但搭了一套 Airflow,结果每个月花时间维护 Scheduler、Worker、元数据库。那是「能跑」阶段的过度设计。
「跑得稳」阶段也有类似的坑,但更隐蔽------不是「不该用」,是「用了但没花对地方」。
曾经做过一个数据入湖链路,单机跑没问题,数据量上来后怕挂。加了 checkpoint------挂了能从断点续跑,不用从头再来。这个复杂度花对了------多写了几十行代码,换来的是凌晨三点不用爬起来手动重跑。但接着有人建议「再加一套自动故障转移,把单机变双活」。这个就花错了------链路本身不要求高可用,挂了停几分钟再起来没关系。双活带来的同步开销、脑裂处理、状态一致性维护,成本远超收益。最后没做,把时间花在了别的地方。
判断一个复杂度该不该花的唯一标准:你知不知道这个复杂度在解决什么问题。 你知道核心链路每天跑多少条、每一条丢了代价多大------你知道,你就能判断。你不知道,你就是在一个场景还没看清楚的时候给系统加了一道锁。不是锁扣上用不上------是锁本身就是成本,你在为「可能」买单,而不是「需要」买单。
五、稳定性也在消耗信任
「跑得准」那篇讲过:准确性出一次错,信任就崩了。
但稳定性也在消耗信任,只是消耗得更慢、更难察觉。
曾经做过一个项目,用到了 GCP 上的一个离线计算服务 Dataproc。选型的时候看中了它启停比 AWS EMR 快:Dataproc 不到一分钟就能起来,EMR 要几分钟。而且启停时间本身也是计费的------有些任务只跑几分钟,光启动就花了两倍的时间成本。
但上线后,Dataproc 频繁因为机器资源紧张或系统升级导致服务不可用------不是你代码的问题,是它底层基础设施的问题。每一次不可用都直接影响了数据的交付时效,客户支持团队承受了巨大的压力。不是一两次,是反复、持续性地出问题。
用户不关心是哪个环节的问题。他们不关心是你代码的问题还是云服务的问题------他们只看到「数据没按时到」。最后公司因为续费单的损失,下定决心把全线迁移到内部云。
Dataproc 启停快是技术上的优势,但在线上频繁不可用面前,那个优势被消耗得一干二净。用户不关心你选了什么底层技术,只关心数据什么时候到。 你选了更快的服务,但交付不了确定性的输出------那个「快」就没意义了。
六、退出信号
什么时候「跑得稳」过了?第1篇给过一个信号:你能安心睡觉了。
这不是玩笑,是一个真实可感的指标。你不躺在床上想「系统会不会炸」------不是因为你加了最牛的监控,是因为你知道即使炸了也有兜底,即使不炸也在被看着。你知道核心链路上有重试、有降级、有告警。你知道一个组件挂了不会拖垮全部。你知道数据丢了有 checkpointer 能续回来。
这些都是你前两个阶段攒下来的认知。你知道了系统的核心在哪、瓶颈在哪、什么能丢什么不能丢------你花钱的时候知道自己在买什么。你不是在买一个「可能更好」,是在买一个「已知需要」。
收尾
「能跑」阶段你在做减法,「跑得准」阶段你在换站位,「跑得稳」阶段你在做加法。生产环境要的是确定性的输出,不是偶尔惊喜。跑得准了,就把它稳下来。