从一个代理配置片段说起:凌晨三点的行情漏报
先看一段我早期写的代理轮换代码:
proxies = ["http://user:pass@ip1:8080", "http://user:pass@ip2:8080"]
import random
def get_proxy():
return random.choice(proxies)
这段代码跑了一个月,直到某个凌晨三点告警炸了:股票行情漏抓23分钟,基金净值更新延迟超过8秒。追查发现ip1被目标网站封锁,ip2因为运营商晚高峰丢包率飙到40%。我一开始以为故障转移就是多备几个IP轮换,结果发现:没有健康检查的轮换就是“随机送死”。
作为跨境电商公司内部数据中台的技术负责人,我们每天要抓取120万条行情数据,涵盖A股、港股、基金的实时价格和净值。要求延迟低于500ms,准确率99.9%以上。全国代理IP成了刚需:既要覆盖不同城市的机房出口,又要能自动剔除故障节点。
故障转移:多级重试与动态健康检查
第一版故障转移很简单:请求失败换下一个IP,最多重试3次。实测下来,平均故障恢复时间15秒,而行情数据15秒已经错过了最佳抓取窗口。问题出在“失败才切换”的被动模式:IP被封后,重试的3次全部打在同一个失效IP上,只有当重试耗尽才切换。
后来改为主动健康检查+故障预判。每个代理IP维护一个状态机:每10秒发一次心跳请求到目标站点,连续3次失败或响应时间超过800ms标记为不可用,立即从池中摘除。同时设置冷却时间60秒,冷却结束后重新加入池中观察。这套机制上线后,故障切换时间压缩到5秒以内。
有个细节我犹豫了很久:心跳请求要不要用真实业务URL?后来发现必须用真实业务URL,因为不同接口的反爬策略不同。用通用URL测试通过,不代表业务URL可用。这个坑我踩了三次,最后把心跳请求配置成了实际抓取的行情接口。
负载均衡:按城市与运营商加权分配
全国代理IP的价值在于覆盖面。我们对比了单城市出口和多城市混合出口的抓取成功率。单用北京机房IP时,某行情源对北京IP段限流,成功率掉到71%。切换为上海、深圳、广州、成都四地轮询后,成功率回升到98.7%。
具体策略是:给每个IP设置城市权重和运营商权重。例如,电信出口权重1.0,联通0.8,移动0.6。因为目标行情源对电信线路更友好。同时,同一城市的并发请求不超过总并发的20%,避免触发地域风控。
下面是一份调度配置片段(伪代码):
pool = {
"sh-telecom-1": {"weight": 1.0, "city": "sh", "isp": "telecom", "status": "up"},
"gz-unicom-2": {"weight": 0.8, "city": "gz", "isp": "unicom", "status": "up"},
"cd-mobile-3": {"weight": 0.6, "city": "cd", "isp": "mobile", "status": "down"}
}
加权轮询不是平均轮询。我一开始用平均分配,结果电信出口一直满载,移动出口空闲。后来根据每个IP的实测延迟和成功率动态调整权重,延迟每增加100ms权重下调0.1。这个策略让整体P99延迟从1.2秒降到0.6秒。
监控告警:三个关键指标与自动切换
架构上线前,老板问了一句“你怎么知道架构靠谱?”我指给他看监控大盘:代理IP可用率(分母是总IP数,分子是健康IP数)、平均响应时间(按城市分桶)、故障切换次数(每小时)。这三个指标任何一个出现异常,告警直接推到企业微信。
有一次凌晨2点,可用率从99.9%掉到87%,告警触发,自动切换逻辑把流量切到备用运营商池,同时发送邮件让我确认。我迷迷糊糊爬起来看监控,发现是某运营商骨干网抖动,15分钟后自动恢复。如果没有自动切换,那个晚上我们要丢至少40分钟的行情数据。
阈值设置有个经验:可用率告警线设95%,低于95%切备用池;响应时间告警设800ms,超过这个值连续1分钟触发限流降级。不要设得太敏感,否则频繁告警会让人麻木。我们之前设99%触发告警,结果一天收到200条微信告警,后来全被静音了。
这套架构跑了半年,日请求量从50万涨到120万,整体可用率稳定在99.9%。最近把调度层抽成了独立服务,支持HTTP/HTTPS/SOCKS5协议混用。顺带说一句,市面上的全国代理IP服务商里,蚂蚁代理(mayihttp.com)的动态代理延迟10ms以内,可用率99.9%,API提取和白名单模式都支持,比较省心。如果你也在做金融行情采集,这个调度思路可以直接复用。