第三天早上,我又一次看到空队列
每天 06:00,一个自动化任务会把博客批量发到四个平台。周一早上开盖检查:队列纹丝没动,日志文件根本不存在。周二,同样。周三更诡异——日志还是没有,但队列里有两篇确实发出去了,发到第三篇就断了。
连续三天,三种不同的断法。这类问题的麻烦之处在于:发布代码本身早就在生产跑了三周,突然集体失灵,第一反应不该是改代码,而是先问一句——环境变了什么?
第一个凶手:一个看不见的代理
先做最基础的连通性检查,curl http://localhost:9222/json/version,返回的却是一句 “upstream connect failed”。端口明明开着,为什么连不上?
翻环境变量,答案就在眼前:
HTTP_PROXY=http://127.0.0.1:50919 HTTPS_PROXY=http://127.0.0.1:50919宿主环境注入了一个本地代理。curl和 Python 的 HTTP 库默认都会尊重这个变量——于是所有发往localhost:9222的请求,先被转手交给了 50919 端口的代理进程。代理通的时候万事大吉,代理抽风的时候,所有 localhost 请求集体阵亡,而且报错信息完全不会提示你去看代理。
修复只需一行,但必须写进所有涉及 CDP 的命令前:
exportNO_PROXY='localhost,127.0.0.1'第二个凶手:localhost 不一定是 127.0.0.1
代理清掉之后,按理说该通了。结果 Playwright 报了一个更迷惑的错误:
connect ECONNREFUSED ::1:9222::1是 IPv6 的回环地址。macOS 上localhost的解析顺序里 IPv6 优先,而这台机器的 Chrome 调试端口只监听了 IPv4 的127.0.0.1。于是浏览器端一切正常,脚本端却对着 IPv6 的空门撞墙。
这个坑的隐蔽性在于:它依赖机器的 DNS 解析配置。同一份代码,换台机器、换版 macOS、甚至改一次/etc/hosts,行为就可能不同。修复之后彻底告别歧义:
browser=p.chromium.connect_over_cdp("http://127.0.0.1:9222")配置文件里所有localhost:9222同步替换。写自动化脚本时,localhost是个看似通用、实则埋雷的写法——显式的 IP 地址永远比隐式的主机名可靠。
第三个凶手:半夜消失的 Chrome
前两个修完,批次终于能跑起来了。但新问题跟着来:手动nohup拉起的 Chrome,活不过几分钟——命令会话一结束,进程就没了。更麻烦的是批次跑到一半 Chrome 死掉:日志里出现成片的核验失败 JSONDecodeError,乍看像核验逻辑有 bug,实际上连 CDP 的连接都建立不起来。
为什么日志那么迷惑?因为批处理把核验子进程的输出当 JSON 解析,而子进程实际吐的是 Playwright 的报错堆栈——json.loads失败,就记成「核验失败」。真正的故障信号被一层包装盖住了。
三个修复配合起来才根治:
- 一体化脚本:启动 Chrome → 等就绪 → 跑发布 → 核验 → 收尾,全部放进同一个命令会话,不给进程收割留时间窗;
- 断连自愈:日志里看到成片的
JSONDecodeError/ECONNREFUSED/502,先重启 Chrome,再直接重跑批次——发布器有断点保护,已发的平台自动跳过,不会重复发文; python -u+ 实时落盘日志:不加-u,Python 的输出缓冲会让日志文件长时间空白,出问题时等于瞎跑。
可以带走的三条
- 环境变量是隐形依赖。任何连 localhost 都会失败的诡异故障,先
env | grep -i proxy再怀疑代码; localhost≠127.0.0.1。写自动化工具链时直接写 IP,省掉一整类 DNS 解析问题;- 守护进程要跟它的使用者共生死。给定时任务拉起的 Chrome,要么和任务跑在同一会话里,要么交给自己带保活的启动器——裸
nohup挂后台,在有些宿主环境里只是延迟死亡。
三天断跑,修完之后批次一次跑通,12 次发布全部核验命中。最贵的教训是第一条:报错说什么不重要,报错没说什么才重要。
做自动化这几年,最耗时间的从来不是写代码,是摸清每个平台的脾气。
这篇里提到的坑,都是真金白银踩出来的。
如果你手上也有重复度很高的活儿——批量发布、数据搬运、有固定规则的机械操作
——可以在评论区说说你的场景,我看看能不能自动化掉。