凌晨3点的告警电话
凌晨3点17分,手机震动把我从梦里拽出来。运维组长老张的声音有点慌:“游戏账号监测程序挂了,IP全部被封,内容审核队列堵了5万条。”我一边开电脑一边骂自己——明知道双十一期间电商审核压力大,代理IP调度方案还是太草率了。
我们运营着20多个游戏账号做合规内容审核(批量检测网页是否有违规广告/外链),每个月要处理约200万条URL。之前用的是蚂蚁代理(mayihttp.com)的动态IP池,API提取模式,单线程跑着一直没问题。但上周业务量翻倍,我临时改成了多线程并发提取,结果代理IP调度架构没跟上——提取IP超时、重试逻辑死循环、目标网站直接把整个IP段拉黑了。这次翻车让我花了整整两天重构调度系统,也让我意识到:代理IP不是“买来就能用”的,架构设计才是稳定性的核心。
故障转移:别让单点拖死全家
出事那天,我的调度代码大概是这样的:从代理服务商API一次提取100个IP,放到队列里,爬虫线程不断pop出来用。如果某个IP返回403,就在队列里剔除,然后继续用下一个。听起来没问题?但实际跑起来,一旦IP池中某个IP段被封,剔除操作会触发大量并发请求重新提取,API响应变慢,线程阻塞,最终导致队列为空,所有任务卡死。
我后来改的故障转移策略:
- 分层IP池:将代理IP按运营商(电信/联通/移动)和区域(城市)分组,每组维护独立队列。内容审核系统针对不同网站用不同分组——比如监测淘宝用浙江电信,监测拼多多用上海联通,避免跨省跨运营商影响速度。
- 健康检查预判:每个IP在使用前先发一个HEAD请求到百度首页,响应时间超过300ms的直接淘汰,不进入业务队列。这个预判把无效IP的命中率从15%降到了2%以下。
- 断路器模式:当某组IP连续失败超过5次后,暂时冻结该组30秒,切换到备用组(比如从电信切换到移动)。我设置了一个计数器,每次失败加1,成功归零。冻结期间不发任何请求到该组,避免雪崩。
这个故障转移方案上线后,同样在双十一峰值(500 QPS),IP调度失败率从之前的7.3%降到了0.4%。不过还有个坑——如果所有组都失败了呢?我在断路器上加了一个紧急熔断:当所有组冻结超过3次,直接暂停任务并发送PagerDuty告警,而不是无限重试。这个边界处理花了最多时间调试,因为一开始我没想到“全部失效”这种极端情况。
负载均衡:加权轮询还是最少连接?
内容审核爬虫有个特点:不同网站的响应时间差异极大。比如监测小网站(响应200ms)和大平台(响应1.5s),如果平均分配请求,慢网站会拖累整个队列。我一开始用简单的轮询,结果发现某些高延迟网站占用了大量IP资源,导致其他线程饥饿。
实测对比后我选用了加权最少连接(Weighted Least Connections),权重根据目标网站的平均响应时间动态调整:
| 策略 | 总请求数 | 平均延迟 | 任务完成时间 | IP消耗量 |
|---|
| 轮询 | 50000 | 1.2s | 16.7h | 820个 |
| 加权轮询 | 50000 | 0.9s | 12.5h | 650个 |
| 加权最少连接 | 50000 | 0.7s | 9.7h | 510个 |
加权最少连接不仅快,而且IP消耗最少——意味着更少被封。原因是慢网站会自然分配到较少的并发连接,避免了IP被占用过久。我写了个Python脚本,每5分钟统计每个目标域名的平均响应时间,然后更新到Nginx upstream配置中。
负载均衡的细节坑
这里有个意外发现:我曾以为连接数越少越好,但某次把权重设得太激进(慢网站并发限制到1),结果导致那个网站的任务积压,最终影响了整体进度。后来我加了最小并发数下限——每个网站至少保留2个并发连接,保证任务不会饿死。这个参数是调了三次才找到最优值:2个并发连接在测试中平衡了吞吐和延迟。
另外,不要用粘性session!内容审核需要每个请求走不同IP,粘性session会导致IP复用频率太高被检测。我在这上面浪费了半天,看日志才发现同一个IP反复请求同一个URL。
监控告警:不仅要报警,还要能定位
第一次翻车时,我的监控只设置了“任务队列长度>1000告警”。结果队列在3分钟内从500飙到5万,告警根本来不及看。后来我重新设计了监控指标,分层告警:
- 第一层:IP健康度——每分钟统计IP成功/失败比率。当失败率超过10%时触发黄色告警,通知到钉钉群。这个阈值是跑了一周数据后定的,10%以下通常不影响业务,超过15%就会开始积压。
- 第二层:调度器状态——监控每个IP组的活跃连接数、队列积压数、提取API响应时间。如果提取API响应超过1秒,说明服务商可能限流,自动切换到备用提取策略(比如从API提取改为账密认证隧道)。有一次该服务商的API短暂波动,这个切换让业务几乎无感。
- 第三层:业务SLA——计算每个任务的处理时长,如果超过5秒(正常是2秒内),直接电话告警。这个阈值我设得比较紧,因为内容审核有实时性要求,用户上传的图片和链接要尽快出结果。
定位日志是关键:我在每次代理请求的日志里加入了trace_id和ip_used字段,这样告警触发时可以直接查日志,看到底是哪个IP、哪个网站导致的失败。以前我们只记请求成功/失败,不记IP,排查问题像大海捞针。现在从发现到根因定位,平均时间从30分钟降到了2分钟。
验证效果:再也没在半夜被叫醒
重构后的调度架构跑了一个月,内容审核系统处理了550万条URL,总失败率0.08%。双十二那天的峰值(900 QPS)也没出问题——凌晨3点我特意定了闹钟起来看,结果一切正常,比预期还稳。
这次经历最大的收获是:代理IP调度不是简单堆IP池,而是要考虑故障转移的幂等性、负载均衡的动态调整、监控的层次化。如果你也在做类似的内容审核爬虫,建议先花两天把架构搭好,别像我一样等到翻车才补课。对了,该服务商的IP池质量确实稳定,但再好的服务商也要有靠谱的调度兜底。