前几天,监控面板上有一台云主机,内存监控冒到 200% 以上。
我们自研的监控面板,有 CPU/内存/磁盘/流量四个图。内存那个图,画的是 Hyper-V 的内存"需求",超过 100% 就意味着这台机内存不够了,该加内存或升级。那天晚上,面板弹出一条红色警告:"内存严重不足,可能影响服务器使用。"
我心想:又一台超卖主机被客户跑满了。准备联系客户加内存。
结果客户说,他看任务管理器,内存才用了 42%。
我有两小时,把这件事从头到尾捋了一遍。
先怀疑读数不准。我们监控是宿主机侧读的 Hyper-V MemoryDemand,不是客户机内部的任务管理器。这个数是"客户机想要多少内存",跟任务管理器"实际用了多少"不是一回事,系统性偏高,通常高 10~20 个点。所以实际 42%,读到 200% 需求,概率不大,但不排除。
我又想,是不是某台机内存真的爆了,宿主都卡了?于是把宿主上所有 VM 的内存需求拉了个表。规律清清楚楚:动态内存开的 VM,内存需求都有值;动态内存没开的(固定内存),需求全是 0。那台 200% 的,一看是动态内存、Min/Max 锁死的,确实在超。
再往后,我开始怀疑自己了。是不是监控那个弹窗脚本,影响了整个页面?
因为客户说:"你那个告警弹出来之后,页面顶上的服务器运行状态就一直'正在加载运行状态',转圈转不出来了。"
我以为是弹窗脚本和页面其它脚本打架——毕竟弹窗是我加的悬浮层,又是半透明又是绝对定位。我干脆把弹窗关掉测试,还是卡。
到这里已花了不少时间。真正抓到问题,是客户发来一个截图,上面一行红字:
Microsoft VBScript 运行时错误 '800a0006'
溢出: 'CLng'
/vpsadm/vps_status.asp, 行 77
就是这一行。
那个页面顶上的"运行状态"接口,里面有一句:
uptime = CLng(val) / 1000
val 是客户机运行时长(秒)。当一台机器连续运行太久,这个秒数超过 CLng 能表示的上限时,VBScript 直接抛"溢出",整个接口中断——返回的不是 JSON,而是一张报错页。前端拿到报错页,解析 JSON 失败,就一直卡在"正在加载运行状态"。
而运行时间特别长的主机,往往内存/CPU 也高(停机少,负载累积)。所以"状态加载不出来"和"内存飙高"这两个现象,总是同时出现在同一批长寿机器上。这就造成了完美闭环:我看到"内存高 + 状态加载不出",很自然地归因为"内存高拖垮了接口",其实是 CLng 溢出这个独立 bug。
重启那台主机,状态立刻恢复了——因为重启把运行时间清零了。
修法也简单:
vtmp = Replace(val, ",", "")
If IsNumeric(vtmp) And Len(vtmp) > 0 Then uptime = CDbl(vtmp) / 1000
CLng 换成 CDbl,顺手去了逗号、加了数字校验。同样的坑,以后运行再久也不会踩了。
这单最后赚的不是修理费,是两条教训:
1. 两个现象同时出现,不等于同因。内存高和状态加载不出,看起来像"内存高拖垮接口",实际是两个独立问题撞在一起。排查别急着归因,先看数据。
2. 老代码里的整数溢出,是最容易被表象掩盖的根因。运行时长这种会一直增长的数,用 32 位存,早晚出事。
而这台主机——它 200%?那个是真的,客户确实该加内存了。只是我差点因为"加载不出来"的假象,把它当成又一起接口故障,错过真正的扩容商机。
评论