政务云的圈子里有个词,叫静默夜。不写在任何红头文档里,只在排期表和值守群里流传:挑全年业务量跌进谷底的那个时段,把攒了半年、憋成大版本的新内核,趁着没人用,一次性换上去。社保、出入境、水电气这些和系统绑死的民生服务,白天离不开,夜里没人碰,能动的窗口拢共三四个小时。错过这一夜,下一个窗口要等两个月;而兼容层一天不铺,旧接口就多扛一天老毛病。
月底的静默夜定在周四,切换窗从零点半开始。志远数科这回是总集成。值守名单贴出来的时候,林默的名字列在「技术方案口「一栏——经过压测那晚,这个名字已经不需要向谁解释是什么编制、为什么在名单上。名单上还有老周,还有机房侧的小赵;例行组的主操还是佟工,口令还是那个念法:每运行一步,等机房值守复诵一遍,再落下一个字。
升级前的那一周,林默把预案翻了三遍。第一遍按流程走,逐条对变更单;第二遍专挑「哪里会死「,把每一步的失败模式列了一遍;第三遍反过来,假设真死在这儿了,手里还有什么能兜。翻到零停机切换那一段,他停的时间最长。
零停机切换,说穿了是让新旧两套内核在同一秒里交接班:新内核接流量,旧接口不摘线,谁也不许眨眼。预案里这一条写得最顺——回退按原路径走,测试环境七遍全绿。可他在评审会上把这条挑了出来,在文档边上批了一行:此路径含旧接口续命逻辑,测试环境未真实触发,建议加灰度预检。评审的结论是排期压了一切:窗口只有这一夜,灰度预检加不进来。主持会议的副局长拍了板,理由现成——测试七遍全绿。林默没再争。散会后他把那行批注原样抄进自己的值守手册,页角折起来。文档可以信「理论上「,他不信;跟文档较劲没有用,能做的只有提前想好它崩的时候,第一铲子挖哪儿。
他顺手还做了另一件事。在升级变更单上增补了一条预案:添加「影子桥接「一项,触发条件写死——内核服务可用率跌破九成五,即视为切换失败,转入影子桥接流程,技术方案口负责。条款编号、运行人、资源来源(边缘余量池)填得规规矩矩,走了正常会签。甲方那位分管领导签得很快:多一道保险,签的人只赚不赔。
这一条是给今晚留的正门。压测那晚他是被架上去的,接管口径是事后补的;这一次,门提前修好,钥匙提前挂在墙上。周四晚上十点半,他进了指挥大厅,比切换窗早了整整两个小时。
保温杯里是浓茶,泡得比平时苦。他把监控墙每一块屏过了一遍:内核集群基线、兼容层心跳、异地容灾链路的巡检态、边缘池四十一台的在线与余量。老周坐在侧位,搪瓷缸照旧磕着豁口;小赵在机房侧盯硬件指针,隔几分钟在对讲机里报一句「一切正常「,声音年轻,压着兴奋。大厅里的人各干各的。厂商来了两家,坐在靠门一排,笔记本开着,屏幕上是自家竞品的参数——来验货的心思没散,验的就是新内核铺开之后,这朵云还缺不缺他们。升级总指挥是局里协调来的一位科级干部,对照流程图背主持词,背到「如遇突发情况「那一段,语速明显慢了半拍。
林默坐在角落,茶喝了一半。他脑子里把切换串行又过了一遍:零点半切读流量,一点十分切写,一点四十一旧接口批量续命,三点整里程碑确认。四个节点里,他把第三步标了红——续命,就是让几十个旧接口伸手到新内核里去,抓它们认了几十年的那套老规矩。
规矩要是还在,一夜平安。规矩要是没了,就是今晚。
零点半,切换准时开始。读流量切得干净,大屏上的曲线平顺地换了主。一点十分,写流量切换,佟工的口令一条一条过,机房复诵一遍不差。一点半,新内核承载全量,监控墙上的绿格稳稳的,大厅里有人开始收拾茶杯,总指挥对着流程图打了第一个钩。
一点四十一分,旧接口批量续命,第三步。头十秒没有动静。第十一秒,兼容层心跳曲线跳了一下。第十四秒,跳了第二下、第三下,间隔越来越密。第三十秒,监控墙上的绿格子开始成片泛黄。
大屏右下角,内核服务可用率的数字往下掉。九九点二,九八,九六。九五——他写进变更单的那条触发线,被数字碾过去的瞬间,大厅里还没人反应过来。九二。八八。八十。
崩的原因,林默比在场所有人都先明白过来,因为答案早就抄在他的手册页角上。兼容层的最底下,压着一行从上一代平台原样沿用过来的默认超时值。多少年了,没人记得当初为什么是这个数,也没人敢动——几十个旧接口围着它长大,把它当成了天经地义的东西:从发起调用到拿到应答,全系统走的是同一根时间轴,所有请求按这根轴排队,谁前谁后,清清楚楚。
新内核是事件驱动的。没有全局时间轴,时间被拆进了每条事件流自己的时间戳里。平时两套东西各过各的,谁也不碰谁;可零停机切换非要它们在同一秒里交接——旧接口被挂上新内核续命,一伸手,去抓那根用了几十年的时间轴。
轴不在了。几十只手同时抓空。超时判定集体错乱,该等的等不住,不该重发的成倍重发,重试撞上重试,状态错位踩着状态错位。调度器一头要按老轴排队、一头要按新事件流转,在两套规矩之间来回横跳,每横跳一次,就带塌一片下游。
这还不算最坏的。最坏的是降级——总指挥在对讲机里嘶声喊「降级!快降级「,可降级脚本第一步就要向调度器索取全局一致性快照,而这一步本身,就踩在那根已经消失的时间轴上。越喊降级,锁得越死。
「拔电源!要不先拔电源——「机房侧一个刚工作两年的年轻运维声音都劈了,手指悬在电源柜的门上。
一只手拍过来,按住他的手腕。老周不知什么时候站到了他身后,搪瓷缸还挂在另一只手上:「拔你个头!拔了全市社保今天全瘫!坐回去,盯你的指针!「年轻人的手缩了回去。老周把缸子往桌上一顿:「都少喊两句。降级进不去是信道的事,喊破天也一样。「
厂商那排没人出声。两家的工程师对着屏幕敲了几行命令,又删了——这时候动一下,是把楼扶住还是推倒,没人说得清。不敢动,比乱动传染得更快,半间大厅的人就这么看着那片黄往红里洇。
可用率掉到七十一的时候,对讲机、电话、键盘声混作一团。总指挥把流程图攥皱了,翻到「如遇突发情况「那一页——那一页只有一行字:激活影子桥接流程,技术方案口负责。
他擡起头,目光越过半间大厅,落在角落里。陈志远也站了起来,隔着人群喊了一声:「默子!「
喊完他就闭了嘴,往主控台外圈一站,把几个正要围过来的厂商工程师拦在三步之外。第二声,他没有喊。
林默把杯子搁在桌角,站起身。穿过人群的那几步,他走得不快不慢。恐慌这种东西会看人下菜,主控台前那个人每乱一分,全场就跟着乱一分。
走到主控台,他先报进程:「触发影子桥接,变更单增补条款,单号肆贰柒壹,现场运行人林默。「总指挥愣了半秒,猛地翻开流程图,看见自己攥皱的纸上确实印着这个单号,嗓子劈着喊了声:「运行!「
正门开了。这一回,开在事前。他坐下,双手落上键盘。人群里有人低声问「能不能退回去「——退不了。回退要把状态顺着旧语义原路搬回去,而旧语义正趴在坑里。退无可退,只能往前,把桥修着走完。
意识最底层,一行指令压过去,无声无息:全开。
耳后的震颤应声而起,语速快得不留缝隙:【影子桥信道确认。池内四十一台,准入三十七台;入池拆十三批,批间隔三秒,四十秒铺完;薄设备缓冲上限三成,时限五十分钟;摄像头类,零接入。】
【三十七台,编号连续,全程可回溯。】末尾又补了一句。
三十七台边缘设备在四十秒里次第醒来——路口的信号机、灯杆的控制器、下了班的备份终端。视野里,全城的节点摊开:哪几台在烧、哪几台余量满格、带摄像头的自助终端整类划走。压测那晚它们是四秒里一齐睁眼的,那个四秒,后来在他自己的文件里记成了教训;今晚拆成十三批,批与批之间套着例行巡检的节拍,频谱上看,只是一串再寻常不过的设备自检。
主控台日志里,今晚每一个动作都挂在肆贰柒壹名下。修好的正门,这会儿就是查日志的人能对到的第一行索引。老周从侧位挪了过来,站在他身后半步,不说话。中途递过来两次纸条,写的全是机房侧的实时读数,字写得又大又快。
止血,三手依次来。
先挂一口钟。旧接口抓空的那根全局时间轴,影子桥在桥面上临时仿真了一口——所有续命中的旧接口,读到的节拍重新有了着落。雪崩的势头,半分钟内刹住了。可用率从七十一慢下来,六十九,六十七,抖着,不再直坠。
接着冻住调度器的排队权,不许它再在两套规矩之间横跳。这一步走的是正式信道——调度器管理接口本就在变更单范围内,佟工按口令运行,复诵,落键。
最后织桥。影子桥分段接管旧接口的调用翻译:旧的那套排队规矩,在桥上一段一段译成新内核听得懂的事件流。能缓存的先缓存,能合并的先合并;读流量的大头引去异地容灾节点,那边是政务云的正规灾备信道,扛全量读不费劲;写流量暂存进标记重放队列,等主库缓过来按原时序回注——这套影子队列,压测那晚跑过一整场,今晚是第二次上线。
一点五十九分,可用率回到七十四。二点〇七分,八十一。二点二十六分,九十。大屏上的红一块一块退成黄,黄再一块一块退回绿。二点三十三分,兼容层心跳恢复固定节拍。对讲机里的声音稀下来了,小赵在机房侧报了一声「内核温度正常「,佟工抹了把脸,口令声重新稳了。
