一段让系统崩盘的代码
先贴一段我第一版写的代理IP调度代码——就这20行,让我们的旅游比价平台刚上线就崩了10分钟。
import requests
def fetch_price(url, proxy, retries=3):
for i in range(retries):
try:
resp = requests.get(url, proxies={'http': proxy, 'https': proxy}, timeout=5)
return resp.json()
except:
time.sleep(2)
return None
proxies = ['http://user:pass@proxy1:8080', 'http://user:pass@proxy2:8080']
while True:
for p in proxies:
result = fetch_price('https://example.ota.com/hotel', p)
if result:
break
当时我天真地以为,轮询两个代理IP就能扛住旅游旺季的请求。结果上线第一天,OTA平台(在线旅游平台)的反爬机制直接把我两个IP都封了,系统陷入无限重试,数据库连接池被耗尽,MySQL挂了。老板脸色铁青,客户抱怨比价不准。这次血的教训让我意识到:IP代理API调度不是简单的轮换,而是一套完整的架构设计。后来我花了两个月,从零搭建了基于故障转移+负载均衡+监控告警的调度系统,才把可用率从30%拉到99.9%。
第一关:故障转移——别再相信单个代理
第一次翻车直接原因就是没有故障转移。两个代理IP一旦被封,整个系统就退化成单点。我一开始设计了一个简单的加权轮询:给每个代理分配权重,响应快的权重高,超时的权重降为0并移出池子。但实际跑起来发现,有的代理IP时延低但成功率低(比如60%),有的时延高但成功率99%。单一的权重指标没法兼顾。
后来我采用了双指标评分系统:
- 健康分:基于最近30次请求的成功/失败记录,失败一次扣5分,成功一次加1分,低于60分自动摘除。
- 延迟分:取最近10次请求的中位数延迟,延迟<200ms得100分,200-500ms得70分,>500ms得30分。
- 综合得分 = 健康分 * 0.7 + 延迟分 * 0.3,然后按得分比例分配请求。
这个方案在旅游比价场景里跑了一周,效果不错——但有个坑:当所有代理IP同时被反爬封禁时(比如OTA集体升级验证码),健康分集体掉到0,系统直接空转。我加了一个兜底策略:当可用代理数量少于阈值(比如3个)时,触发紧急扩容,通过API从蚂蚁代理等供应商拉取一批新IP,并降低健康分衰减速度,避免过度敏感。实测下来,故障恢复时间从15分钟降到了30秒以内。
def score_proxy(proxy):
health = proxy_stats['success'] / (proxy_stats['total'] or 1) * 100
latency = median(proxy_stats['latencies'])
latency_score = 100 if latency < 200 else (70 if latency < 500 else 30)
return health * 0.7 + latency_score * 0.3
第二关:负载均衡——别让某个节点吃满
解决了故障转移,发现另一个问题:某些代理IP被频繁调用,导致同一IP在短时间内发出大量请求,触发OTA的速率限制(Rate Limit)。我们的比价平台需要同时查询5家OTA(比如携程、飞猪、美团、去哪儿、同程),每个OTA要求不同的地域IP(比如查北京酒店要用北京IP)。这就涉及两个维度的负载均衡:IP级别和地域级别。
我最初用随机算法,但随机在高并发下会导致IP调用分布不均匀。测试了加权轮询和一致性哈希后,最终选择了最小连接数算法(Least Connections):每次给当前未完成任务数最少的代理IP分配新请求。结合地域亲和性,每个地域(比如北京、上海、广州)维护一个代理池,请求先按地域路由,再在池内做最小连接数调度。
划个重点:最小连接数 + 地域隔离这套组合在旅游比价场景中,把单个IP的请求间隔控制在平均8秒以上,低于OTA的常见反爬阈值。我在蚂蚁代理后台看到它的API支持按城市提取,配合我们的地域亲和性策略,提取一次就能拿到200个指定城市的IP,再通过本地调度算法分流。对比效果:
| 调度算法 | 单IP最大并发 | 请求成功率 | 平均延迟(ms) |
|---|
| 随机 | 52 | 87.3% | 245 |
| 加权轮询 | 38 | 91.5% | 210 |
| 最小连接数+地域亲和 | 12 | 99.2% | 178 |
数据说明:最小连接数算法把每个IP的并发压力降低了70%以上,成功率提升了近12个百分点。这个结果让我很意外,因为我一开始以为轮询就够用了。
第三关:监控告警——看不见的坑才是致命的
做完调度后,我以为万事大吉了。结果有一天凌晨3点,业务方反馈比价数据大面积延迟。我检查日志发现:代理IP池的可用量从2000个骤降到了30个,但调度系统没有触发告警,因为健康分衰减策略在缓慢降低分数,没有触发阈值。等到我发现时,已经持续了20分钟。
这次教训让我重新设计了监控告警体系,分三层:
- 第一层:代理池健康度。每分钟统计可用IP数量、总IP数量、当前使用率。阈值:可用量<100或使用率>80%时告警。
- 第二层:请求级别质量。按地域统计最近5分钟的成功率、平均延迟、超时率。阈值:成功率<90%或平均延迟>500ms时告警。
- 第三层:业务异常检测。比价平台依赖多个OTA的响应,如果一个OTA的请求成功率突然下降(比如从99%降到80%),可能是该OTA反爬升级或代理IP被黑名单。
监控数据通过Prometheus采集,Grafana可视化。我把蚂蚁代理API的提取记录也打进了监控——通过API每次提取的IP数量、延迟、提取成功率,可以反向判断代理供应商侧的问题。有一次发现蚂蚁代理的API响应时间从50ms飙到了500ms,我立刻联系客服,原来是他们的一个数据中心在做维护,提前给了切换预案。如果没监控,我可能还在傻傻重试。
总结:架构不是一次性设计出来的
回顾整个改造过程,我的核心体会是:IP代理API调度架构必须持续迭代。故障转移、负载均衡、监控告警这三块,任何一个短板都会让整体可用率崩盘。你不可能在第一版本就设计完美,重要的是建立反馈闭环——让监控数据驱动算法调整,让故障复盘驱动架构改进。
现在我的比价平台每天要跑200万次请求,覆盖全国30+城市,代理IP调度从未出过大问题。蚂蚁代理的API(mayihttp.com)配合我自研的调度器,已经稳定运行了8个月。如果你也在做类似的高并发地域切换业务,建议直接抄我的这套架构,至少省去两个月的踩坑时间。当然,你肯定会遇到我没想到的坑——到时候欢迎来我的博客留言,咱们继续填坑。