先给结论:不要指望事后翻日志就能还原现场,你要做的是把“出错那一刻的响应”变成一份可复查的快照。具体做法是让检查任务按短周期运行,每次命中异常就立刻保存状态码、响应头、时间戳和重定向链路,而不是只记一条“失败”。这样你手里就有了能对照的证据,而不是一句“昨天好像报过错”。
大多数站点的死链巡检是每天或每周跑一次,跑完只留一个汇总数字。问题在于,间歇性错误往往和某个条件绑定:可能是每天凌晨的备份任务占满了数据库连接,可能是整点缓存集中失效,也可能是上游接口在某个时段限流。巡检恰好避开了这些窗口,自然什么都查不到。
还有一种更隐蔽的情况:错误确实被记录下来了,但记录方式让你无法判断它是不是真问题。比如日志里只有一行“500”,没有请求的完整 URL、没有响应头、没有前一次跳转的来源。你无法区分这是页面真的挂了,还是某一瞬间的偶发超时。这两种情况对应的处理动作完全不同,所以证据的粒度决定了下一步怎么走。
在动手之前,先明确你要捕捉的对象。拿你手上任意一个已知会间歇性报错的 URL 作为样本,回答三个问题:
如果这三个问题里有一个你能给出明确答案,就可以把采集任务的范围缩小到对应条件,而不是全天候全站扫描。范围越小,单位时间内能采集的样本越密,捕捉到短暂错误的机会就越大。
核心动作是:把检查频率提高到足以覆盖可疑时段,并且每次异常都落盘一份完整记录。假设你怀疑错误出现在每天 02:00 到 03:00 之间,可以这样安排:
这里有一个容易忽略的取舍:请求频率提高会带来额外负载。如果目标站点本身资源紧张,高频请求可能让问题变得更严重,甚至制造出原本不存在的错误。所以频率要根据站点承受能力设定,并且优先在低峰时段加密,而不是全天候拉满。
动作的结果会直接影响下一步。如果你成功抓到了出错瞬间的响应头,里面出现了一个只在特定时段才有的 Retry-After 或某个上游服务的错误标识,那问题方向就明确了,接下来是去找那个上游或那个时段的任务;如果抓到的响应和正常时完全一样,那说明错误不在 HTTP 层面,可能要转向内容比对或前端渲染层面的排查。
抓到证据之后,不要急着修。先判断这条记录属于哪一类:
把这三类分开记录,比笼统地标记“有问题”有用得多。因为后续动作完全不同:暂时性错误改调度,真实死链改内容,中间状态继续观察。
假设你的站点每天凌晨做数据库备份,备份期间某些动态页面会返回 503。巡检任务在白天运行,所以一直显示正常。你把检查窗口调整到凌晨,并保存了出错时的响应头,发现里面有一个来自反向代理的超时标识,同时备份任务的结束时间比预期晚了十几分钟。
这个假设里,证据链是:出错时段 + 响应头特征 + 备份任务时间重叠。三者同时成立,才能把原因指向备份窗口。如果只有时间重叠,没有响应头特征,那还不能排除是巧合。这也说明为什么单靠“某段时间出错”这个现象本身不足以定位原因,必须配合出错瞬间的原始响应。
基于这个判断,下一步动作可以是把巡检任务避开备份窗口,或者调整备份任务的资源占用。但要注意,这只解决了巡检误报的问题,如果真实用户在这个时段访问也会遇到 503,那还需要从服务层面处理,而不是只改检查时间。
调整之后,不要只看到“不再报错”就结束。有效的验证是:在原来出错的时段,用同样的采集条件再跑一轮,确认响应头里不再出现之前那个错误标识,且状态码稳定。如果只是把检查任务挪走了,那验证的只是检查任务本身,不是问题本身。
另外,如果错误和外部依赖有关,比如某个第三方接口在特定时段不稳定,那么即使你这边调整了,对方的问题可能依然存在。这种情况下,证据的作用是帮你判断是否需要增加降级逻辑或缓存策略,而不是单纯地“修好了”。