断线断的到底是哪一段,先分清再谈优化
很多人遇到代理"掉线",第一反应是服务商不行,换一家。结果换了三家还是断,问题其实出在自己这段代码上。
代理链路上一共有三段:你的程序到代理服务器、代理服务器到目标站点、代理服务器自己这条出口 IP 的存活状态。三段里任何一段出问题,程序看到的都是同一个报错——超时或者连接被重置。想让它不掉线,得先知道断在哪一段。
最典型的四种情况:一是程序每次请求都新建一条连接,握手握手握手,几百次之后服务端的空闲连接被回收,下一次写入瞬间报错;二是代理节点本身抖动,延迟从 80ms 突然飙到 3 秒,你设的读取超时没跟上,直接抛异常;三是连接池开得太小,请求排队排到超时;四是出口 IP 已经失效,握手阶段就失败。
这四种情况对应的解法完全不一样。下面按"先保连接、再探存活、最后做补救"的顺序说。
长连接:别每次请求都重新握一次手
HTTP 协议本身支持在一条 TCP 通道上连续发多次请求,这叫 keep-alive。默认情况下,很多语言的标准库是开启的,但如果你每次请求都新建一个会话对象,那等于每次都从零开始——通道刚建好就被丢掉,keep-alive 完全没用上。
做法很简单:整个任务只建一个会话对象,重复用。Python 里就是 requests.Session(),并且给这个会话挂一个连接池。
import requests
from requests.adapters import HTTPAdapter
session = requests.Session()
# 池子里留 20 条空闲通道,最多同时开 50 条
adapter = HTTPAdapter(pool_connections=20, pool_maxsize=50, max_retries=0)
session.mount("http://", adapter)
session.mount("https://", adapter)
session.headers.update({"Connection": "keep-alive"})
proxies = {
"http": "http://用户名:密码@代理地址:端口",
"https": "http://用户名:密码@代理地址:端口",
}
resp = session.get("https://你的目标接口", proxies=proxies, timeout=(3.05, 8))
print(resp.status_code, resp.elapsed.total_seconds())
这里有两个参数值得说。连接池大小别乱填,一般按你的并发数来,池子至少要等于同时在跑的线程数,否则多出来的请求只能干等。超时一定要写成元组,第一个是连接超时、第二个是读取超时,只写一个数字的话两者会混用,排查问题时会很难受。
参数给个参考值:内网或同城节点,连接超时 3 秒、读取超时 8 秒就够;跨地区节点可以放到连接 5 秒、读取 15 秒。千万别写 timeout=None,那不是稳定,是把问题藏起来——真断了程序会一直卡在那儿,比报错更麻烦。
还有一个细节:有些服务端的空闲回收时间比较短,比如 5 秒没动静就断开。这时候你的程序如果 6 秒后才复用这条通道,就会撞上一个"看起来还活着其实已经死了"的连接。解决办法要么是把空闲间隔压短,要么在发请求前做一次轻量校验,这就是下面要说的事。
心跳检测:别等报错了才知道断了
心跳的意思很直白——每隔一段时间,主动发一个极小的请求,看看对面还答不答话。答了就继续用,不答就提前把它踢出候选池。这样你就不用等到真正发业务请求的那一刻才发现通道已经死了。
判断标准建议用三条:状态码正常、响应时间在阈值内、返回内容对得上。只看状态码不够,有些异常节点会返回一个 200 的空壳页面,看着成功其实白跑一趟。
import time, requests
def alive(proxy, timeout=3):
"""返回 True 表示这条代理还能用"""
try:
s = time.time()
r = requests.get(
"https://你的轻量检测地址",
proxies={"http": proxy, "https": proxy},
timeout=timeout,
)
return r.status_code == 200 and (time.time() - s) < timeout
except Exception:
return False
间隔多久探一次?30 秒是个不错的起点。太密了浪费资源,太疏了等于没探。如果你的任务是长时间挂着跑的,可以按"活跃连接 30 秒一次、空闲连接 5 分钟一次"分两档,省下来的开销很可观。
连续失败几次才算死?建议设成 2 次。一次失败可能只是网络抖了一下,直接判死会造成频繁换 IP,反而更不稳;两次连续失败基本能确认这条通道废了。判定死亡之后别急着丢掉,先把它放到"观察区",隔 1 分钟再探一次,很多时候它能自己缓过来。
检测地址不要选大文件、不要选带登录的页面,找一个返回体很小、响应很快的接口就行。心跳消耗的流量很小,但它省下来的失败重试要多得多。
故障转移:断了之后怎么在三秒内接上
前面两步是在"尽量别断",这一步是"断了别慌"。故障转移说白了就是:出错了立刻换一条,别在原地死磕。
具体要设四个东西。备用池:手上常备 3 条以上可用代理,按延迟排序,好的排前面。重试次数:单次任务最多重试 3 次,超过就放弃并记录,无止境重试只会把线程卡死。退避间隔:第一次失败等 0.5 秒,第二次等 1.5 秒,别一失败就疯狂重发。失败标记:失败的代理降权而不是直接删,给它留个复核的机会。
def fetch(url, pool, retry=3):
for i in range(retry):
proxy = pool.pick() # 按权重挑一条,延迟低的优先
try:
r = requests.get(url, proxies={"http": proxy, "https": proxy},
timeout=(3.05, 8))
if r.status_code == 200 and r.text:
pool.mark_good(proxy) # 成功就加权
return r.text
pool.mark_bad(proxy)
except requests.RequestException:
pool.mark_bad(proxy) # 失败只降权,不立即删除
time.sleep(0.5 * (2 ** i)) # 退避:0.5s / 1s / 2s
return None
如果你的任务对连续性要求很高,比如要维持一段时间的会话状态,那就别用短效代理了,换成长效静态 IP 或固定 IP 代理更合适。短效动态 IP 的优势是数量多、换得勤,适合一次一个请求的独立采集;一旦涉及连续多次请求共用同一出口,静态类的更省心。这个选择错了,后面再怎么调参数都是白费。
| 类型 | 存活时长 | 适合的活 | 长连接友好度 |
|---|---|---|---|
| 短效动态 IP | 分钟级,用完即换 | 单次独立请求、公开数据采集 | 一般,通道刚热起来就该换了 |
| 长效静态 IP | 数小时到数天 | 需要维持会话的连续任务 | 好,连接池能长期复用 |
| 固定 IP 代理 | 长期稳定不变 | 固定出口、需要白名单的场景 | 最好,参数一次调好基本不用管 |
选服务商时,盯住这几个数字就够了
参数调得再好,也扛不住上游一直抖。挑服务商的时候,别只看价格和 IP 数量,下面几项才是真正影响"掉不掉线"的。
第一看可用率。99.9% 听着像营销词,换算一下:一天 86400 秒,0.1% 就是 86 秒不可用。如果你的任务是 7×24 挂着的,这个数字直接决定了你要写多少容错代码。第二看 IP 纯度,纯度低的池子里混着大量已被目标站点标记的地址,你连上去握手就失败,表现出来跟"断线"一模一样。
神龙HTTP 这边提供的是 3000 万+ 正规授权代理 IP 储备,可用率 99.9%、IP 纯度 99.8%,覆盖 300+ 城市级精准定位,国内三大运营商正规授权,支持 HTTP/HTTPS/SOCKS5 三种协议。放在这篇的主题里,比较实用的是个人中心那套可视化用量统计和实时监控——失败率突然抬头、某批 IP 延迟整体走高,图表上能直接看出来,不用靠猜。配合技术团队 7×24 小时支持,真遇到成片失败的时候,能比自己在日志里翻半天快得多。
另外,它的 API 兼容主流爬虫编程语言,文档和示例代码都给了,上面这几段连接池和重试逻辑接进去基本不用改结构。产品线分短效动态、长效静态、固定 IP 代理三类,按前面表格对号入座就行,别一股脑全用短效。
常见问题
Q:连接池开得越大越稳吗?
不是。池子上限超过服务端允许的并发,多出来的连接会被直接掐掉,反而增加失败率。一般设成同时在跑的线程数就够了,再多只是占内存。
Q:心跳检测多久一次合适,会不会很费流量?
活跃连接 30 秒一次、空闲连接 5 分钟一次是比较省心的搭配。检测请求的返回体很小,一条连接一天下来的开销通常在几十 KB 级别,比起失败重试浪费的流量可以忽略。
Q:已经做了重试,为什么还是会中断?
大概率是重试的还是在同一条死掉的代理上打转。检查一下你的重试逻辑里有没有真的"换一条",以及备用池里是不是本来就没几条能用的。另外确认超时不是设成了 None,那样失败不会触发重试。
Q:短效动态 IP 能不能也做长连接?
可以,但收益不大。短效 IP 的存活时间本身就是分钟级,通道刚复用几次就该换了,连接池的价值发挥不出来。这种场景把精力放在快速失败和快速切换上更划算,长连接优化留给长效静态或固定 IP 代理。






