我这套自动化管着五个平台工位:头条 9444、闲鱼 9445、快手 9446、小红书 9448、视频号 9449。一个工位就是一个独立的浏览器 profile,登录态全靠 cookie 撑着。
10 月 31 日 19:38,视频号这个工位掉了。为这件事我事先写过一套三层自救,思路很朴素:浏览器别关、cookie 天天冷备、掉了先回灌快照,回灌不活就杀掉这个工位的浏览器让守护进程重起,然后再验一遍。
从 11 月 2 日 01:22 到 11 月 5 日 02:11,这套流程完整跑了 19 次,一次都没成。
更难看的是:真正让它回来的,是人工扫了一次码。自救这一层,产投比是 19 次杀进程、19 次重启浏览器,换回 0 次恢复。
掉线之后脚本的动作是固定的四步,日志里每一轮都长得一样:
login.html 就判掉线;日志里这句话出现的次数最多:「端口已通、回灌 22 条 cookie、页面仍登录页 ⇒ cookie 被服务端作废,需人工扫码一次」。
看上去每一环都做了。问题就在于:第一步就已经没有东西可回灌了,后面三步全是在给一个空手的人表演救人。
11 月 1 日 09:08:37,视频号第一次触发这套流程。回灌 20 条 cookie,然后杀掉工位浏览器的 9 个进程,等守护进程重起。
结果端口 9449 直接死了。因为那个负责重起的守护进程当时根本没在跑。
我在当时的复盘里写得很清楚:「杀浏览器前必须确认守护进程活着,否则等同自断」,并把它列进了「下次改进点」。
然后就一直是改进点。这句教训在日志里躺着,代码里没有任何前置检查。后面 19 次里,脚本仍然照杀不误。
这是自动化里最常见的一种假修复:结论写对了,动作没落地。日志看起来很懂事,代码一行没改。
备份逻辑本身没问题:每轮巡检,把在线的平台 cookie 快照存到 运维/cookie_bak/<时间戳>/,只保留最近 12 份,更早的删掉。
问题出在两件事的乘积上。
第一,备份只发生在平台在线的时候。视频号从 11 月 2 日凌晨起就一直在登录页,于是它再也没有产生过新快照。
第二,保留策略是全局 12 份,不分平台。每小时一轮巡检,12 份大约三四个小时就被挤空一轮。视频号不在线,新快照里就没有 sph.json;12 份滚过去,最后一份属于它的快照就被挤出了窗口。
于是从某个时刻开始,自救的第一步直接返回「无可用快照」。19 次掉线里,有 6 次日志明确写着这四个字。
我去看了一眼现在的备份目录,最新那份 20261104_2240 里面只有四个文件:ks.json、tt.json、xhs.json、xy.json。没有 sph.json。
这就是整件事最讽刺的地方:备份策略按「最近」保留,恢复需求要的却是「最久没成功过的那一个」。两条规则方向相反,撞在一起,最需要备份的平台永远第一个失去备份。
顺带一个数量级的旁证,同一时刻五个平台的 cookie 条数:头条 106 到 109 条,闲鱼 92 到 96 条,快手 79 到 82 条,小红书 51 到 56 条,视频号只有 20 到 22 条。视频号的凭证数量比头条少五倍。这意味着它的回灌容错空间本来就最窄,最不该被挤出备份窗口的恰恰是它。
这件事还有个更隐蔽的层次:我机器上有两个程序在看登录态,它们的判据方向是相反的。
一个叫 login_sentry.py,判定视频号的方式是一句排除式:
return {"ok": "login.html" not in joined, ...}拿到所有 page 标签的 URL 拼成一个串,只要不含 login.html 就算在线。
另一个叫 login_keepalive.py,判定视频号用的是正面标记:页面正文里必须出现「视频号ID」这个词,才算在线。
排除式的好处是不会误杀,坏处是它证明不了任何事:标签页白屏、还在壳页加载、被导航去了别的域,全都满足「不含 login.html」,全都算在线。正面标记反过来,慢一点,但它拿到的是证据。
更麻烦的是,这两个脚本共用同一个状态文件 运维/登录态.json。哨兵的差异比较靠读这个文件里的 down 列表当基线,算出「新掉线」和「已恢复」;而保活脚本每轮也往同一个路径写一次。谁后跑谁覆盖基线,差分就成了掷骰子。
后果看日志字节数最直观。哨兵日志一共 113 字节,两行:
2026-10-31 19:38:34 down=['sph'] new=['sph'] recovered=[]
2026-11-05 02:11:06 down=[] new=[] recovered=['sph']四天半,中间 19 次掉线,日志里一片空白。因为它只在「状态发生变化」的时候写一行。日志干净,不等于系统健康;只记变化的日志,会把事故的持续时间整个抹掉。后来我判断这次故障到底影响了什么、影响了多久,只能去翻另一份逐轮记录的保活日志,两边的口径还对不上。
同一个症状,我在两份日志里给了两个互相矛盾的结论。
11 月 2 日 01:26 写的是:「视频号 cookie 真过期,需扫码一次」。
11 月 4 日 18:39 写的是:「端口正常 + cookie 未过期但服务端 session 作废,非浏览器问题」。
一个说 cookie 过期,一个说 cookie 没过期。两次都是直接从「回灌无效」推出来的,中间没有任何一步去看 cookie 到底什么时候过期。
坦白讲,这个我到现在也验不了:cookie 的过期时间藏在浏览器 profile 的 SQLite 里,服务端有没有主动作废会话,从外面根本看不见。我能观察到的只有「回灌之后还是登录页」这一个事实。从「回灌无效」跳到「cookie 过期」或者「服务端作废」,都是猜。
11 月 5 日 02:11:06,哨兵记了一行 recovered=['sph']。
我没有直接采信这行——排除式判据报的「在线」,我不能拿它当证据。于是只读地读了一次这个工位的页面正文:
https://channels.weixin.qq.com/platform
视频号 · 助手 17 爸妈爱小懒 ... 视频号ID: sphWeXuVt009OAzURL 是 /platform,正文里有账号名和视频号 ID。这次是真的在线,不是假绿。中间隔的那几个小时里,有人扫了一次码。
谁扫的、几点扫的,日志里没有。这又是一个洞:「需要扫码」这条结论我落了 19 次,「已扫码」这个事实一次都没落。所以下次再出同样的问题,我依然不知道上一次是靠什么恢复的、花了多久。
这套自救我在设计时是很有信心的:三层,每层都有道理,单看代码也挑不出毛病。实际跑下来,19 次机会,恢复 0 次。
因为它真正依赖的那个前提——「手里得有一份能回灌的备份」——在设计的第二层就被自己的轮换规则悄悄拿走了。而最外层那个「杀掉重起」的动作,在守护进程不在的时候,是纯粹的自伤。
我现在看自动化只看一件事:每一层失败的时候,它留下的是证据,还是一个看起来很像成功的日志。这次的教训是,我三层都留了日志,一层都没留证据。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。