逻辑驱动:让代码自主推演答案
|
去年过年那会儿,我原以为值班能消停点——结果大年三十晚上十点,监控突然炸锅,告警灯闪得跟迪厅似的。三台负载均衡的CPU飙到95%,可流量才平时的三分之一。我盯着屏上并排开的三个终端,第一反应是有人手滑把备份脚本跑生产环境了?结果查日志发现,是某个新上线的服务,逻辑分支写炸了——它本该根据请求头里的X-Trace-ID做路由,结果代码里写反了条件判断,把所有流量都怼到同一台后端机上。 当时我盯着那坨代码看了十分钟,越看越气。这不就是典型的“逻辑驱动”反面教材吗?开发写代码时根本没推演过极端情况——比如X-Trace-ID不存在时该走默认路由,结果他直接把else分支写成了空操作。更绝的是,这服务还开着健康检查,但健康检查的接口和业务接口是同一个端口,只是路径不同。所以当业务接口因为负载高开始丢包时,健康检查也跟着挂,负载均衡直接把这台机标记为“不可用”,流量全涌到剩下的两台上——恶性循环,三台全挂。 老张在旁边吃泡面,筷子挑着面说:“这不就是达尔文进化论吗?代码自己跑着跑着,把能活的都筛出来了。”我差点把键盘砸了——优胜劣汰?这他妈是劣币驱逐良币!那些写得更严谨的服务,反而因为“不够激进”被流量压垮。比如隔壁组的服务,人家严格做了限流和熔断,结果在这次故障里被负载均衡直接踢出集群——因为它的健康检查接口响应慢了200ms,超过了阈值。 但你说“逻辑驱动”完全没用吗?也不至于。后来我们复盘时发现,有个老服务在这场灾难里活了下来。它用的是十年前的代码,逻辑简单到粗暴:不管什么请求,先查本地缓存,缓存没有再走数据库,数据库连不上就返回503。那天晚上它的缓存命中率飙到99%,数据库连接池根本没被打满。我原以为这种“老古董”早该被淘汰了,结果它靠最原始的逻辑推演——先保自己不死,再管其他——硬是扛住了流量洪峰。
文章配图,仅供参考 那天凌晨三点,我蹲在机房角落修跳板机密码(又过期了,这破系统非得每90天改一次)。冷风机在头顶嗡嗡响,外卖袋子上的水汽把桌角弄湿了一片。我突然想到,其实“逻辑驱动”和“优胜劣汰”的本质,是代码能不能在运行时自己“想”清楚两件事:第一,我现在要干什么?第二,我干不了的话该怎么办?比如那个挂掉的新服务,它的逻辑是“如果有X-Trace-ID就走A路径,否则什么都不做”。但它没“想”过:如果A路径不通怎么办?如果什么都不做会导致什么后果?而老服务的逻辑是“先查缓存,缓存没有再查数据库,数据库不行就报错”。它的每一步都有备选方案,哪怕备选方案很粗暴(比如直接报错),但至少保证了系统不会因为某个环节卡死而崩溃。 后来我们强制要求所有服务必须实现“逻辑自洽”:每个分支必须有明确的退出条件,每个外部依赖必须有超时和重试机制,每个健康检查接口必须和业务接口解耦。开发们骂声一片,说这会增加20%的代码量。但过年那场故障之后,类似的连锁反应再没出现过——至少没因为逻辑漏洞搞出过全站挂掉的事故。 不过话说回来,这世上哪有完美的逻辑?上周五晚上,我们刚上线了一个“智能路由”服务,号称能根据实时负载动态调整流量分配。结果周一早上发现,它把所有流量都路由到了磁盘IO最高的那台机上——因为它的逻辑是“选当前负载最低的机子”,而负载计算里忘了排除swap交换的干扰。那天我盯着监控看了两小时,看着那台机的磁盘灯疯狂闪烁,突然觉得,所谓“逻辑驱动”,到最后还是得靠人去兜底。 现在每次值班,我都会下意识检查两件事:一是服务的逻辑分支是否覆盖了所有异常情况,二是健康检查接口是否独立于业务逻辑。但我也知道,这只能防住80%的问题——剩下的20%,要么是逻辑太复杂没人能推演清楚,要么是外部环境突然变化(比如云厂商的磁盘突然变慢)。就像去年过年那晚,如果那三台负载均衡的CPU没飙到95%,如果健康检查的阈值设得宽松点,如果新服务的逻辑分支没写反……可哪有那么多如果? 所以我现在跟开发们说:“别迷信逻辑驱动,但也别完全不信它。写代码时多问自己两句——如果这里挂了,系统会怎么走?如果那里慢了,用户会看到什么?想清楚了再写,比事后补监控有用得多。”至于他们听不听?反正我已经把椅子轮子卡着的那根网线换了——上次值班时差点被它绊倒,摔碎了半个键盘。 (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


5G已重构移动互联,别用4G思维写代码