一个误当通配符的方括号,如何让服务每半小时重启一次
今天排查一个「每隔半小时就断一次」的问题。用户报障听起来像网络故障,查到最后,真凶是一行不起眼的进程匹配代码——一个被当成正则通配符的方括号,让守护脚本一遍遍把进程从 2 个堆到了 17 个,最后内存见底,内存保护机制强制重启。这篇把排查过程摊开,因为它背后是一条能直接抄走的工程教训。
一、症状是「定期断」,指向却不是网络
第一反应当然是查链路:信号、连接、掉线日志。结果这些全是好的——连接正常、在线正常、三项外部探测都通。
转折点在运行时长:设备自己报的已运行时间很短,可内存可用量却低到吓人。把两件事对上,就发现真相不是「连不上」,而是「机器周期性重启了」——每半小时一次的「断」,其实是整机重启的那几十秒空窗。
教训第一条:别被报障的字面描述带着走。用户说「断网」,真正的病灶可能在进程、内存、内存保护机制。先看运行时长和内存,再决定往哪查。
二、根因:守护脚本里那个被误读的方括号
真正的元凶是一行用来「确认代理进程还活着」的守护逻辑。它用 pgrep -f 去匹配一个配置文件名,而那个文件名里带了一对方括号。
问题在于:括号在正则里不是字面量,是字符类通配符。所以「找到那个名字的进程」这句判断,实际变成了「找到任意一个长得像的字符」——结果永远匹配不到真正的存活进程。
于是守护脚本坚定地相信「进程死了」,每隔几十秒就重新拉起一个新的。旧的没退、新的又来,进程数从 2 个一路堆到 17~18 个,内存被慢慢吃光,直到逼到内存保护机制强制重启。一个方括号,导演了整场「每半小时断一次」。
三、修法:固定字符串匹配,别让正则掺和进来
解法一句话:判断「进程活没活」,用固定字符串匹配,不用正则。
具体做了三件事:
- 把匹配从正则模式换成
grep -F固定字符串,括号回归字面量,能精确命中真进程; - 把那个有 bug 的守护停掉,换成去重 + 缺失冷却的逻辑:先数「活着的进程到底有几个」,超过一个就只去重、不拉新;
- 顺手加了内存分级处理,内存快见底时优先只处理进程而不是整机重启,把「重启空窗」这个二次伤害也消掉。
验收是硬指标:运行时长越过原来的 30 分钟周期仍在增长,进程数稳定在 2,内存可用量回到正常水位,三项外部探测全部通过。
四、为什么这条值得记进经验库
这类坑有个共同点:抄来的守护脚本,往往在最不起眼的一行藏着通配符陷阱。文件名、路径、参数里但凡出现 [ ] * $ 这类字符,一旦进了正则语境,语义就悄悄变了。
我们把这次的经验固化成了三条规矩:
- 进程活性判断一律
grep -F或整段匹配,宁可多写几个字符,不让正则通配符碰配置名; - 守护脚本必须带「去重」和「冷却」,防止判断失灵时连环拉起;
- 修完一处,不是只留一份记录,而是把修法回灌进可复用的工具里,让下一处同类问题一步解决。
这也是我们一直强调的:修一个问题,和把修法沉淀成下次能直接调用的能力,是两件必须一起做的事——土拨鼠的自我改进循环走的就是这条路,具体形态见 功能页。
五、一个 30 秒自查
下次遇到「服务周期性抽风」,按顺序问自己三句:
- 先看运行时长和内存,是真「断」还是在「周期性重启」?
- 有没有守护脚本在用正则匹配进程名或配置名?里面有没有被误读的特殊字符?
- 守护逻辑有没有去重和冷却?失灵时会不会连环拉起?
大多数「莫名其妙的周期故障」,都能在这三问里找到答案。想把这类排查流程交给一个能长期跑下去的助手,可以看 定价页 算一笔账;想直接装一个跑起来,就 下载 试手。
土拨鼠 Marmot