先给结论:做内容审核这类高频轮换的业务,决定封禁率的是调度层的熔断粒度,不是IP池的绝对规模。我们矩阵有22个账号,每天要过一遍约8万条URL和图文的内容合规预检,跑过三档供应商,池子从200万条涨到3000万条,封禁率只从4.7%降到3.9%;后来把调度层整个重写,同一个供应商同一个池子,封禁率掉到1.2%。钱花在池子上是线性收益,花在调度上是阶跃收益。
为什么池子越大反而越没感觉
目标站点的风控不是按“你一共用了多少IP”来判的,是按单个IP在短窗口内的行为密度判的。我们的检测接口给的单IP限额是20次/分钟,超了直接返回429,并且把这个IP拉进2小时黑名单。注意这个2小时——它意味着一次429的代价不是丢一个请求,是这个IP在接下来两小时里彻底作废。
池子从200万涨到3000万,本质上只是让你在“作废”之后有更多替补,但作废这个动作本身一次没少。我们最早的土办法是每次请求随机取一个IP,实测下来40%的提取额度浪费在已经进了黑名单的IP上,等于花钱买了一批已经死掉的地址。
说实话我一开始判断错了。第一版架构里我把七成预算压在池子规模上,调度层就是个random.choice,跑了半个月发现成本涨了60%,可用率只从96.2%挪到97.1%。这个坑我们踩了三次才认。
三池分离:把流量按“容忍度”切开
现在的做法是按业务对“会话连续性”的要求把流量切成三份,各自走不同的池,不混用。
| 池类型 | 承载流量 | 单价 | 实测失败率 | 典型用途 |
|---|
| 短效动态 | 70% | 0.0022元/IP | 2.4% | 大批量URL正文抓取 |
| 隧道代理 | 25% | 16元/天 | 0.7% | 需要保持会话的连续翻页 |
| 独享长效 | 5% | 按条包月 | 0.3% | 账号绑定类、登录态操作 |
切开的直接好处是故障隔离。短效池大面积失效时,隧道池和独享池不受牵连,矩阵里最贵的登录态操作不会跟着一起挂。切之前我们遇到过一次短效池集体超时,连带把账号维护任务全带崩了,那晚上补了四个小时。
熔断器:60秒滑动窗口,连续3次摘除
每个IP在本地维护一个60秒滑动窗口的失败计数,连续3次失败就摘除并写入本地黑名单15分钟。这里有个细节:不要照抄供应商的黑名单时长。供应商按2小时拉黑是全局视角,你自己按目标站点粒度可以压到15分钟,因为对方的风控计数器往往也是短窗口的,实测15分钟复用的成功率能回到78%,比直接丢弃省三成提取成本。
class IpBreaker:
def __init__(self, window=60, threshold=3, ban=900):
self.fail = {} # ip -> [ts,...]
self.banned = {} # ip -> expire_ts
self.window, self.threshold, self.ban = window, threshold, ban
def ok(self, ip):
return self.banned.get(ip, 0) < time.time()
def report(self, ip, success):
now = time.time()
if success:
self.fail.pop(ip, None); return
ts = [t for t in self.fail.get(ip, []) if now - t < self.window] + [now]
self.fail[ip] = ts
if len(ts) >= self.threshold:
self.banned[ip] = now + self.ban
self.fail.pop(ip, None)
并发阈值:拉到200反而全崩
我们做过一组梯度测试,固定5000条URL,只改全局并发数:
- 并发50 → 429率0.8%,整体耗时26分钟
- 并发100 → 429率3.1%,耗时17分钟
- 并发150 → 429率7.6%,耗时19分钟
- 并发200 → 429率11.2%,耗时31分钟
拐点在100到150之间,过了150不仅封得多,总耗时还反弹——因为重试的请求挤占了正常请求的通道。我们的经验公式是全局并发 ≈ 当前可用IP数 × 12,那个12是从每分钟单IP 20次的安全线往下打六折得来的,宁可慢一点也别触发429。
监控只埋三个指标就够,多了看不过来:429率(超5%告警)、IP首次成功率(低于85%告警)、P95响应延迟(超800ms告警)。第三个指标最容易被忽略,但它是池子质量劣化的最早信号,比封禁率早大概两天出现。
故障排查清单
- 先看IP首次成功率,掉到85%以下基本是池子脏了,换提取批次
- 再看429率是否集中在某几个目标域名,集中就是并发配高了,别怀疑IP
- P95延迟飙升但成功率正常,通常是出口带宽问题,不是IP问题
- 以上都正常但业务失败,去查自己的黑名单是不是误杀,我们干过一次
成本账:理论值和账单差40%
240万次请求/月(8万×30),平均每条1.3次请求,约312万次。短效池按每IP复用12次算,提取约26万条,理论572元;隧道480元;独享池几百元。理论总价1500元上下,但实际账单稳定在2100元/月左右,多出来的四成是重试和空转提取——预取了一批IP但业务侧没用完就过期了。把预取数量从“按峰值”改成“按滑动平均×1.3”,这块浪费压掉了一大半。
这套架构在日均8万条的量级是够用的,再往上到百万级,调度层本身会变成瓶颈,得换成消息队列异步分发,本地黑名单也得挪到Redis。至于短效池,我们现在跑的是 mayihttp.com,0.0022元/IP的价位和实测<10ms延迟对这个场景够用;哪天量级翻十倍,我大概率会把主力挪到独享池,毕竟到那个阶段,稳定性的边际价值远高于单价。