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" }