其实就是一个普通的牛马下午,没有发布,没有配置变更,我正准备关监控摸鱼,报警突然响了,接口成功率在缓慢下滑,不是断崖式的那种,而是那种很恶心的、持续掉一点点。最糟糕的是,服务器指标一切正常。CPU、内存、连接数都在安全线以内,Grafana面板看上去甚至有点“岁月静好”。
这种场景,做过运维的人应该都熟:
系统没炸,但用户在骂。
我第一反应不是怀疑代码。原因很简单——这种“钝刀子割肉”的错误率,很少是代码Bug,更多时候是某个外部条件在悄悄拖慢系统。于是我打开日志,干的第一件事也很简单:
不看错误内容,先看IP。
日志里5xx的请求其实不少,但当我把IP拉出来做聚合的时候,事情开始变得有意思了。大量错误并不是随机分布的,而是明显集中在少数几个来源上。这些IP看起来不像普通用户,特征非常一致,就像是从某个“统一出口”出来的一样。
到这一步,我心里已经有个模糊判断了:
这大概率不是应用层逻辑的问题,而是某一类请求在系统性失败。
如果是代码Bug,IP分布通常会非常散;
如果IP开始聚堆,那就说明问题是“有边界的”。
接下来,我顺着IP往下挖。
这些IP的归属很集中,来自同一地区,同一运营商,而且请求几乎都命中了同一个接入链路。这件事本身就很关键,因为它直接解释了一个现象:
为什么整体成功率在掉,但绝大多数用户其实是无感的。
问题不是“系统在变差”,而是“系统对一部分网络在变差”。
我这时候已经基本可以确定,这口锅不该第一时间扣在后端服务上。但为了保险,我还是做了一个很原始、却非常有效的验证——从不同网络环境去访问同一个接口。
结果很干脆。
同样的接口、同样的参数,不同来源的IP,表现完全不一样。有的请求秒回,有的请求卡在超时边缘。这种差异,一旦和IP对应起来,就几乎不可能是代码问题了。
事情查到这里,其实已经进入“网络问题”的领域了。
继续顺着链路追,我发现这些问题请求虽然最终打到了我们的服务,但它们走的路径明显更绕。回源链路跨了区域,RTT高得不太正常,相当于用户在“明明离你很近”的情况下,被强行送去兜了个远路。
最后和云厂商一起确认,问题原因也印证了我们的猜测:
某个出口路由异常,导致特定地区的流量被错误调度。
从报警到定位到根因,全程我几乎没改一行代码,也没动数据库。真正起作用的,是那一开始对IP的“多看了一眼”。
事后复盘
这次事故,我反而越来越确信一件事:
在运维视角下,IP地址不是一个“字段”,而是一条线索。
它把用户、网络、CDN、出口、服务节点全部串在了一起。只要你顺着它往前走,很多看起来模糊的问题,其实都会自己露出轮廓。
如果那天我一上来就钻进代码里,去抓慢SQL、调GC参数,大概率只会在错误的方向上消耗时间。真正的问题,并不在我能控制的那一层。
后来我们在系统里做了一点很小、但很关键的改动。
日志里不再只是冷冰冰地记录IP字符串,而是提前把它转成“可以分析的信号”。当类似问题再次出现时,我们甚至不用再人工判断,只看聚合结果,就能知道这是应用问题,还是网络在搞事情。
这次事故算不上惊心动魄,但对我个人影响挺大。它让我意识到,作为程序猿,如果你愿意稍微站在运维视角看问题,很多“看似复杂的线上故障”,其实都有非常朴素的切入口。
而IP,往往就是那个最便宜、却最锋利的入口。
PS:
这类排查能顺利推进,其实还有一个前提,就是我们本身的IP数据本身要足够稳定、及时。我们内部现在用的的是 IP数据云IP地址查询数据库,之前购入后我们直接本地集成,和现有日志、分析系统兼容度还不错。到现在用了大半年,更新频率和定位准确性都比较靠谱,至少在实际运维排查里,没怎么拖过后腿。有相关需求的同学可以参考了解一下。