既然回到了书房,就把那套分布式采集系统彻底“微服务化”。团队那边刚吵完架,心力交瘁,但一摸到键盘,感觉又活过来了。管理真他妈是毒药,把人耗干了,还是代码诚实,一行是一行。
这次重构的核心就一个:让这堆脚本别他妈再半夜给我打电话了。之前为了抢流量,爬虫写得跟敢死队一样,只管冲,不管埋。IP被封、验证码弹窗、目标站DOM结构微调、甚至对方服务器一个502,整个链条就卡死,然后告警邮件像雪片一样,最后还得我凌晨三点爬起来查日志。这种日子过够了。
先从异常处理开刀。以前就是try…except一把梭,打印个错误信息就完事。现在得分层。网络层异常,比如连接超时、SSL错误,自动触发IP池切换,并且把失败请求丢进一个延时重试队列,队列本身用Redis做持久化,防止服务重启丢数据。解析层异常,比如BeautifulSoup找不到预期的CSS选择器,立刻截图保存当前HTML快照,同时调用备用解析方案——上正则,或者直接上机器学习训练的提取模型(虽然那模型准确率也就七成)。数据层异常,比如插入数据库时唯一键冲突,不能简单跳过,得记录下冲突数据,触发人工复核流程。
光有异常处理不够,得让系统自己“报告病情”。我接入了Prometheus+Grafana。每个采集器实例都暴露一堆指标:请求成功率、平均响应时间、各类异常计数、队列堆积长度。在Grafana面板上,哪个环节变黄、变红,一目了然。光有监控还不行,告警得智能。我用了Alertmanager,根据不同的异常类型和频率分级告警。偶尔的网络抖动,只记录不通知;连续解析失败,发邮件;要是整个IP池都被封了,或者数据库连接池耗尽,直接给我和企业微信都发高危告警。告警信息里必须带上关键上下文:出错的任务ID、触发的异常类型、相关的两三行日志。
还有状态恢复。我给每个采集任务设计了一个状态机:pending(等待)、running(执行中)、retrying(重试中)、succeeded(成功)、failed(最终失败)。任务失败后不是直接丢进死信队列,而是根据错误类型进入“重试中”状态,有最大重试次数和指数退避延迟。所有这些状态变迁,都写入数据库的一张任务流水表。任何时候,我都能拉出这张表,看清一个任务是怎么死掉的,死在哪个环节。
搞这些,团队里的小孩不理解,觉得我折腾,“功能能跑不就行了?”他们不懂。这种对健壮性的偏执,是以前被流量绑架、被运维压力折磨出来的后遗症。你不再相信“偶然”,你知道所有小概率事件在足够大的请求量下都会必然发生。你要做的,就是在代码里为这些“必然”提前挖好战壕。
把采集器、解析器、存储器、调度器、监控代理一个个拆成独立的微服务,用Docker打包,用docker-compose串起来。看着它们各司其职,通过HTTP API或者消息队列通信,心里居然有种诡异的平静。管理人会背叛你,需求会反复横跳,但这套系统,只要我设计得足够周密,它就能在无人值守的深夜里,沉默而稳定地运转下去。
这大概就是技术人最后的骄傲和避难所了。外面是扯不完的皮和填不完的坑,回到这里,你还能当个工匠,用逻辑和细节,构建一个可控的小世界。虽然我知道,这种控制感也是幻觉——明天可能就有新的反爬策略上线——但至少今晚,我能睡个整觉了。














