上周财务把上半年的采购明细发过来,我顺手做了个透视:代理IP这块累计支出 28400 元,其中 11600 元耗在了失败重试上。团队五个人,主业是学术论文采集,同时跑 12 个库——从 PubMed 到 ScienceDirect,再到几个中文库,每个库的限制策略都不一样。数字摆在投影上的时候,屋里没人说话。
更扎心的是失败分布。半年里累计触发封禁 10100 次,其中三个库贡献了 78%。也就是说,我一直在为九个基本没问题的库,平摊三个难缠库的代价。这篇文章就是那次复盘之后,调度架构怎么改的过程记录。
把账摊开:钱到底花在哪三个库上
先上一张表。这是我按库维度拉了半年的日志,按封禁次数排序,只留前三个和其余汇总。做完这张表,我第一反应是:之前那套"一个大池子打天下"的做法,从一开始就想错了。
| 库 | 限速特征 | 半年封禁次数 | 封禁后恢复耗时 | 单次重试成本(元) |
|---|
| A 库(英文全文) | 单 IP 约 60 次/小时 | 3140 | 45–90 分钟 | 0.9 |
| B 库(引文库) | 需登录态 + IP 稳定 | 1980 | 12–24 小时 | 3.4 |
| C 库(中文库) | 高频触发验证码 | 2760 | 约 15 分钟 | 1.2 |
| 其余 9 个库合计 | 宽松或无限速 | 2220 | 10 分钟内 | 0.4 |
我一开始的判断是"池子不够大",差点又去加采购量。后来把 IP 复用率和封禁时间戳对齐才发现,A 库被封的 IP 里,有 63% 在 30 分钟内被重复调用过两次以上——不是池子的问题,是路由层没做隔离。B 库更典型,封禁几乎全部发生在会话中途换 IP 之后,一换就掉登录态,一掉就重新鉴权,鉴权次数一多直接进黑名单。
所以真正要改的不是采购清单,是调度。这也是我后来跟人聊代理IP推荐时的第一个观点:先看清目标站点的限速特征,再谈选谁。
架构重写:把一个大池子拆成三条通道
新架构的核心思路很简单——按目标库的限制强度打标,走不同的通道,参数完全分开。整条链路是:采集器 → 路由打标(按域名映射通道 ID)→ 三条通道各自的取 IP 策略 → 结果回传健康度 → 动态调权重。
- 通道 A(高频轮换):承载 6 个宽松库,单 IP 用完即弃,绝不二次调用。
- 通道 B(长会话粘性):承载 3 个严格库,出口 IP 至少要撑住 20–40 分钟,中途换 IP 等于自杀。
- 通道 C(兜底低频):只做失败后的补采,并发压得极低,避免污染主通道的 IP 信誉。
| 通道 | 会话时长 | 并发上限 | 超时 | 重试次数 | 失败后动作 |
|---|
| A | 单次即弃 | 120 | 8s | 2 | 换 IP 立刻重试 |
| B | 20–40 分钟 | 15 | 25s | 1 | 换 IP + 重新登录(带 30s 退避) |
| C | 5–10 分钟 | 8 | 30s | 0 | 落盘进补采队列,次日处理 |
路由打标那层我写得很土,就是一个字典加装饰器,二十分钟能落地:
CHANNEL_MAP = {
"ieee": "B", "sciencedirect": "B", "springer": "B",
"pubmed": "A", "arxiv": "A", "cnki": "A",
}
def pick_channel(domain):
return CHANNEL_MAP.get(domain, "C")
def fetch(url, retry=2, backoff=30):
ch = pick_channel(extract_domain(url))
for i in range(retry + 1):
ip = pool.acquire(ch)
if try_request(url, ip):
health.report(ch, ip, ok=True)
return True
health.report(ch, ip, ok=False)
time.sleep(backoff if ch == "B" else 0)
return False
通道 B 的 IP 来源我换过一次。早先用短效池硬撑,登录态每周掉三四次;后来改成隧道代理,出口固定,掉线率才压下来。这块我用的是蚂蚁代理的隧道,16 元/天起,延迟实测稳定在 10ms 以内——它在这个维度上不是最便宜的,但对"会话不能断"这个需求是合适的。
熔断:我第一版做成了全局,结果停产 40 分钟
说个翻车。第一版熔断是全局的:任意一个库连续失败就整体降速。上线第三天,B 库触发阈值,整个采集任务停了 40 分钟,A 库那六个本来跑得好好的也跟着歇了。老板在群里问"今天数据怎么没出来",那种感觉不太好受。
后来改成按库维度独立熔断,阈值调了三轮才定下来:
- 连续 15 次 返回 429 或 403 → 该库熔断 10 分钟,其余库不受影响。
- 验证码命中率 5 分钟滑动窗口内超过 35% → 熔断 20 分钟,同时把该通道的 IP 段整体降权。
- 半开恢复:熔断期满后放 3 个探针请求,全成功才恢复,失败则翻倍延长熔断时间(10→20→40 分钟封顶)。
还有个细节值得单独说:重试必须先换 IP 再退避,不能原地重试。我们早期是原地重试两次,结果同一个 IP 把 429 打成了 403,封禁时长从 15 分钟变成 12 小时。这个坑我踩了三次才长记性。
监控三个指标,以及迁移后的实际数字
监控面板我只留了三个核心指标,多了没人看:按库维度的可用率(低于 97% 告警)、脏 IP 率(首次请求即失败的比例,超过 8% 告警)、TTFB 的 p95(超过 2.5s 告警)。前两个进企业微信群,第三个只在夜间触发电话。上线两个月,夜间被叫醒两次,都是 C 库那边的问题,值。
| 指标 | 迁移前(半年均) | 迁移后(近两个月) |
|---|
| 整体请求失败率 | 40.8% | 6.2% |
| 月均代理支出 | 4733 元 | 3100 元 |
| B 库月均封禁 | 330 次 | 21 次 |
| 单篇论文平均获取耗时 | 18.4s | 7.1s |
成本降了不是因为我换到了更便宜的服务,而是失败的请求少了——重试才是真正的烧钱项。现在接入方式上我固定用 API 提取 + 账密认证双通道,白名单只给固定出口的机器用,三种方式按场景分开,别混着来。
回到代理IP推荐这个话题。我现在的回答顺序变了:先说清楚你采的是什么站点、它按什么维度限速,再谈池子大小和单价。如果有人上来就报 IP 数量,那份推荐清单大概率对你没用。想自己对比参数的,可以去 mayihttp.com 拉一下接口文档,实测比看宣传页靠谱。
这套架构在我们这个量级——日均 60 万请求、12 个库——够用了。如果你一天要跑千万级,通道数和熔断粒度得再细分一层,别照搬。