array(5) { ["chapterid"]=> string(8) "49247945" ["articleid"]=> string(7) "7279460" ["chaptername"]=> string(7) "第1章" ["content"]=> string(10725) "凌晨两点十七分,陈序被手机震醒。
不是闹钟。
是生产告警。
那种震动不大,却足够把一个后端从梦里拽回事故现场。
手机压在键盘旁边,贴着桌面嗡嗡作响。旁边那杯咖啡已经凉透,杯口结了一圈浅褐色的印子。外卖袋没扔,筷子横在盒盖上,键盘缝里还卡着半块饼干渣。
他抬起头,脖子僵得像被人塞了一截钢筋。
办公室只剩一排显示器还亮着,远处自动贩卖机发出低低的电流声。窗外高架偶尔有车灯划过去,白光一闪,又很快被夜色吞掉。
陈序眯着眼看手机。
告警群已经炸了。
“支付接口 500。”
“订单回调超时。”
“用户开始反馈了。”
“监控大盘全红。”
“@陈序 你电脑还开着吗?”
最后一句让他沉默了两秒。
程序员听见这句话,就跟医生听见“病人还有呼吸吗”差不多。
他揉了把脸,拿起咖啡灌了一口。冷的,苦得发酸。他皱着眉咽下去,手指已经本能地落到键盘上。
“在。”
只回了一个字。
群里安静了一秒。
下一秒,消息刷得更快。
“老陈,用户下单失败。”
“财务那边也炸了,说有几笔状态对不上。”
“我本地是好的。”
陈序盯着最后那句,笑了一下。
很短,没什么温度。
生产事故里,“我本地是好的”这句话,通常只说明一件事:现场不在本地。
他没继续看群。真正的现场不在群里,在日志里。
公司这套系统叫“星河订单中心”,名字起得挺浪漫,代码写得很现实。十年前的老项目,换过四任负责人,接过七八套业务,里面有多少历史包袱,没人说得清。
陈序来公司三年,至少有两年半都在给它救火。
他熟悉这套系统,就像熟悉自己胃疼的位置。
先看网关。
大量 500。
再看订单服务。
线程池堆积。
再看支付回调。
超时、重试、重复请求。
都烦。
但都不新鲜。
老系统嘛,什么病都有。
陈序皱着眉,指尖飞快敲过命令。日志窗口一层层铺开,红色错误像雨点一样往下砸,砸得人眼睛发酸。
电话又响了。
项目经理。
陈序看了一眼。
不想接。
但不能不接。
“陈序,现在影响大吗?”电话那头压着声音问。
陈序盯着屏幕上的错误数量,停了半秒。
影响大不大,这种问题在凌晨两点问出来,答案一般都不适合发语音。
“还在看。”他说,“先别让客服承诺恢复时间。”
“多久能好?”
“我要是知道,就不用看了。”
电话那头沉默了。
陈序没等对方继续问,直接挂了。
屏幕上,日志还在滚。
他把几段异常栈拉到一起比对。刚开始都很正常。空对象、超时、回调失败,虽然难看,但都有迹可循。
直到一行日志从窗口底部跳出来。
java.lang.NullPointerException: Cannot invoke because target is null
at RealityService.java:404
陈序的手停住了。
不是因为空指针。
空指针太常见了。Java 程序员这辈子遇见的空指针,数量大概能绕工位三圈。
真正不对的是后面那个文件名。
RealityService.java
他盯着那行字看了几秒,怀疑自己困糊涂了。
项目里没有这个类。
至少他没见过。
陈序切到 IDE,全局搜索。
没有。
他又搜了一遍,只搜 RealityService。
还是没有。
分支里没有,依赖里没有,历史提交里没有。连那些不知道哪年前同事留下来的测试类、临时类、废弃包里,都没有。
他把手从键盘上拿开了一下。
办公室很静。
静得能听见空调口里那点细微的风声。
“见鬼了。”
他说得很轻。
没人回答。
陈序重新切回日志。
那行异常消失了。
取而代之的,是满屏新的错误。
java.lang.NullPointerException: Cannot invoke because target is null
at RealityService.java:405
java.lang.NullPointerException: Cannot invoke because target is null
at RealityService.java:406
java.lang.NullPointerException: Cannot invoke because target is null
at RealityService.java:407
行号在往后走。
一行一行。
像有什么东西,正沿着一段不存在的代码,慢慢往外爬。
陈序的困意彻底没了。
他没有立刻慌,第一反应是截图、保存日志、拉取上下文。做了这么多年救火,他很清楚一件事:事故现场会骗人,但痕迹不会。
只要日志还在,就总能找到根因。
可就在他按下保存的瞬间,日志窗口闪了一下。
所有异常栈同时消失。
屏幕只剩下一行字。
NullPointerException at RealityService.java:404
陈序看着它。
那行字没有滚动,没有刷新,就停在黑色窗口中央,白得刺眼。
他按了下键盘。
没反应。
鼠标也不动了。
陈序低头看了一眼鼠标线,又看了一眼主机灯。
都正常。
“别闹。”
他伸手去拔键盘。
指尖刚碰到线,屏幕里的字忽然变了。
NullPointerException at RealityService.java:404
reason: reality is null
陈序的背脊一点点绷紧。
现实为空。
这不是任何正常系统会打出来的日志。
哪怕是实习生半夜发疯,也不该把这种东西写进生产环境。
群里还在刷消息。
手机屏幕亮着。
“老陈,接口恢复了吗?”
“用户投诉量上来了。”
“老板问现在什么情况。”
“陈序?”
“陈序你还在吗?”
陈序没回。
他看见监控大盘右上角多了一个服务。
原本那里应该只有订单、支付、库存、会员、消息几个服务,都是他看了无数遍的名字。可现在,在最底下,多出了一行灰白色的小字。
RealityService
状态:UNKNOWN。
没有实例数。
没有 IP。
没有负责人。
没有部署时间。
它像一块不属于这个系统的骨头,被硬塞进监控页面里。
陈序伸手去点。
指针刚碰到那一行,办公室的灯闪了一下。
不是全灭。
只是很短的一下。
短到如果不是他正好抬头,可能会以为是眼花。
可那一下之后,办公室变得不太一样了。
远处打印机自己响了一声。
茶水间那盏坏了半个月的灯亮了,又灭了。
头顶空调风突然变冷,吹在后颈上,像机房里漏出来的风。
陈序在显示器黑色边框上看见自己的倒影。
脸色不太好,眼底有熬夜熬出来的红血丝,头发被压得乱七八糟,胸前的工牌歪着。
倒影里,他身后那排空工位也很安静。
安静得过分。
陈序慢慢回过头。
没人。
他重新看向屏幕。
监控页面已经没了。
日志也没了。
整块显示器变成了纯黑色,像一口没有底的井。
几秒后,黑屏中央浮出一行英文。
Welcome to the Source World
陈序没有立刻动。
他盯着那行字,脑子里飞快过了一遍可能性。
病毒?
远程控制?
显示器故障?
有人恶作剧?
每个猜测都很勉强。
他打开手机,想拍下来发群里。
可相机刚对准屏幕,手机画面里却不是黑屏。
而是一片深蓝色的天空。
天空上挂着一条巨大的进度条。
Compiling world... 99%
陈序的呼吸停了一下。
手机屏幕里的画面轻轻晃动,像有人在另一边拿着镜头。远处有城市,有断裂的高架,有像代码一样从天上落下来的雨。风声从手机听筒里钻出来,夹着细碎的电流声。
他放下手机。
显示器上的英文已经不见了。
取而代之的,是一段新的日志。
Loading JVM...
Searching Java Adapter...
Target found: ChenXu
陈序看着最后那行,终于没忍住骂了一句。
“你还知道拼音?”
下一秒,屏幕深处传来一声敲击。
咚。
像有人在里面敲玻璃。
陈序往后退了半步,椅子轮子碾过地面,发出刺耳的声音。
咚。
第二声。
这一次,他看清了。
黑色屏幕里,有什么东西贴了上来。
先是一团模糊的影子。
没有五官,没有轮廓,像一块被水泡开的墨。它在屏幕里慢慢舒展,透明的手掌贴在玻璃另一侧,指节细长,手腕以下却什么都没有。
陈序的喉结动了一下。
他应该跑。
这是正常人的反应。
可他坐在工位前太久,遇到问题的第一反应早就被训练坏了。
他下意识去看日志。
屏幕角落里,新的异常行疯狂跳出。
NullPointerException
NullPointerException
NullPointerException
一行接一行。
不像系统报错。
像某种东西在重复自己的名字。
那只手贴着屏幕,指尖一点点往外顶。玻璃没有碎,却像水面一样凹了出来。黑色的指尖穿过屏幕,带着一层湿冷的光。
陈序猛地站起来,撞翻了椅子。
椅背砸在地上。
声音在空办公室里炸开。
可群里没人听见,电话那头没人听见,整层楼好像只剩他和这块正在渗出黑影的显示器。
手机还在震。
项目经理又打来了电话。
陈序低头看了一眼,屏幕上显示“来电中”。
再抬头时,那只手已经伸出了半截。
没有皮肤,没有骨头,透明得像一段被切断的引用。它悬在半空,指尖对准陈序胸口,轻轻一勾。
陈序胸前的工牌忽然亮了一下。
不是公司那种劣质塑料反光。
而是一圈很淡的金色字符,从工牌边缘浮出来,像有人用看不见的手写下一行代码。
他看不清完整内容,只看到开头几个字母。
try {
黑影停了一瞬。
像是被什么东西挡住了。
但只是一瞬。
下一秒,屏幕彻底黑了。
办公室所有灯同时熄灭。
陈序在黑屏里看见自己的倒影。
也看见倒影背后,多出了一只没有手腕的手。
那只手搭在他的肩膀上。
冰冷、轻飘,像一段从未初始化过的空引用。
屏幕深处,有个声音贴着他的耳朵响起。
“现实服务,调用失败。”
“目标为空。”
黑色利爪猛地收紧。
陈序的手机从掌心滑落,砸在地上。
屏幕亮着。
告警群里跳出最后一条消息。
“陈序?你刚刚发的是什么乱码?”
下一秒,显示器里刷出一行新的日志。
Java Adapter loaded.
陈序还没来得及回头,整个人就被拖进了屏幕。" ["create_time"]=> string(10) "1785508758" }