代理网关切换时如何排查陈旧 DNS 缓存

代理网关域名已经指向新地址,运行中的任务却仍可能使用旧路径。原因通常不是一个缓存,而是多层状态叠加:权威 DNS、递归解析器、操作系统、应用运行时、HTTP 客户端以及已经建立的连接池,都可能按不同周期保留信息。
本指南面向使用住宅代理、轮换代理开展合规数据采集、市场研究与广告验证的运维团队。目标是定位哪一层保留了旧状态,证明新网关可以工作,并保证切换期间不会出现不安全的直连回退。
先区分三类故障
不要把所有解析错误都叫作“DNS 陈旧”:
- 正向缓存陈旧:仍返回或复用旧的 A、AAAA 地址。
- 负向缓存持续:域名恢复后,先前的 NXDOMAIN 或无数据结果仍被缓存。
- 连接复用:DNS 已更新,但客户端继续使用连向旧地址的 TCP、TLS 或 HTTP/2 连接。
三种问题需要不同处理。一次性重启全部工作进程可能掩盖原因,还会制造重试洪峰。
画出全部解析与复用层
针对代理网关域名记录:
- 权威 A、AAAA、CNAME 与 TTL;
- 生产任务使用的递归解析器;
- 容器、虚拟机或主机的解析配置;
- 运行时使用的解析 API;
- 客户端库的 DNS 缓存策略;
- 连接池空闲时间与最大寿命;
- 代理会话寿命和粘性规则;
- 负载均衡器或网关健康行为。
Node.js 的 dns.lookup() 通常使用操作系统解析能力,而 dns.resolve*() 会执行网络 DNS 查询,配置路径并不相同。因此,诊断脚本和真实应用如果使用不同 API,结果可能不一致。
libcurl 还维护内存 DNS 缓存。官方文档说明默认缓存时间为 60 秒,且不直接采用 DNS 记录 TTL。当前版本对权威“域名不存在”结果与临时、本地解析故障的缓存方式也不同。解释测试前必须记录客户端版本。
建立切换前基线
在每个生产地区运行小规模授权金丝雀,并只保存脱敏证据:
timestamp
workload_region
resolver_alias
gateway_hostname_alias
answer_family
answer_set_hash
dns_lookup_ms
new_connection_or_reuse
connected_gateway_alias
proxy_auth_result
observed_exit_market
request_result
不得记录代理密码、令牌、Cookie、客户标识或完整敏感目标 URL。
A 与 AAAA 必须分别验证。IPv4 成功不能证明 IPv6 路径正常,地址顺序变化也可能改变运行时优先尝试的地址族。
分阶段执行网关切换
1. 提前降低权威 TTL
应预留足够时间,让旧 TTL 在常见解析器中到期,并从多个生产地区确认新值。修改地址时才降低 TTL,不能缩短已经按旧值缓存的记录。
2. 先加入新地址,再移除旧地址
架构允许时,让新旧健康网关短期重叠。全量迁移前验证新网关的代理认证、TLS 主机名、允许的方法、带宽、出口选择和日志。
3. 不改 DNS,单独固定一次金丝雀连接
使用安全的客户端机制,把网关域名和端口仅在单次金丝雀中绑定到新地址。必须保留域名用于 TLS 和代理认证,不能在生产配置中直接换成裸 IP。固定测试可以把网关可用性与解析器行为分开。
4. 比较冷进程与热进程
用同一请求比较:
- 没有应用缓存的新进程;
- 已有 DNS 缓存的热进程;
- 为金丝雀关闭连接复用的热进程;
- 正常生产连接池。
如果只有生产连接池仍连接旧地址,应先检查连接寿命,而不是盲目刷新 DNS。
5. 证据收敛后才移除旧地址
要求所有必要地区都获得预期地址集合、新连接成功、代理出口正确、应用结果稳定,并保留明确回滚窗口。
单独诊断负向缓存
NXDOMAIN 必须有自己的时间线。RFC 2308 规定了使用区域 SOA 信息进行负向缓存。权威解析曾返回域名不存在时,即使记录马上恢复,部分客户端也可能要等负向缓存到期后才能看到。
处理负向缓存事件时:
- 确认权威结果是 NXDOMAIN、无数据、超时还是本地故障;
- 检查决定负向缓存寿命的 SOA 值;
- 查询生产递归解析器,而不只使用公共诊断解析器;
- 比较全新解析路径和受影响应用;
- 负向记录有效期间避免紧密重试;
- 在预期到期时间后验证恢复。
不能把每次查询失败都转成地址轮换。临时解析器故障与权威域名不存在是两种不同信号。
让代理保持失败关闭
DNS 故障时,强制代理任务不得删除代理设置或直接访问目标。验证:
- 代理域名无法解析时,任务停止或进入有上限的队列;
- 旧地址不能绕过代理认证;
- 备用网关经过明确批准和独立测试;
- 恢复期间
NO_PROXY范围不会扩大; - 重试包含指数退避、抖动和总预算;
- 排队任务有过期时间和花费上限。
首试成功率与最终成功率要分开记录。最终 100% 成功仍可能掩盖数分钟错误路由和大量重试。
决策表
| 观察 | 可能层级 | 下一步 |
|---|---|---|
| 递归查询仍返回旧地址 | 上游缓存 | 等待有效 TTL 或修正权威数据 |
| 递归结果已新,应用结果仍旧 | 系统或运行时缓存 | 检查解析 API 与进程缓存 |
| 查询已新,连接仍到旧地址 | 连接池 | 受控排空或缩短连接寿命 |
| A 已更新但 AAAA 仍旧 | 地址族路径 | 分别测试 IPv4 与 IPv6 |
| 域名恢复后仍失败 | 负向缓存 | 核对 NXDOMAIN 寿命和到期时间 |
| 代理域名失败后流量直连 | 策略缺陷 | 停止任务并修复失败关闭路由 |
验收清单
- [ ] TLS 与认证继续使用同一正确网关域名。
- [ ] 新旧 A 与 AAAA 集合已记录。
- [ ] 计划窗口后权威与递归结果一致。
- [ ] 冷进程和热进程都已测试。
- [ ] 新建连接与复用连接可区分。
- [ ] 新网关通过认证和出口地区验证。
- [ ] 直连回退已阻断。
- [ ] 重试和队列预算可防止恢复风暴。
- [ ] 日志不包含凭据或敏感 URL。
- [ ] 所有地区证据收敛后才移除旧网关。
- [ ] 回滚标准和负责人已记录。
相关控制可参考 98IP 的代理故障切换恢复演练、SOCKS5 远程 DNS 测试和代理服务 SLA 验证手册。
常见问题
应用 DNS 缓存必须完全遵循记录 TTL 吗?
不一定。客户端库和操作系统可能有独立缓存策略。应记录生产技术栈的真实行为,不能假设权威 TTL 控制所有层。
刷新 DNS 就够了吗?
不够。DNS 已更新后,现有连接仍可继续使用旧网关。先在金丝雀中关闭连接复用;如果原因确为复用,再渐进排空连接池。
是否应该把 DNS 缓存设为零?
通常不应作为通用修复。禁用缓存会增加解析器负载和延迟。应选择符合切换目标的有限缓存与连接寿命,并在负载下验证。
可以把代理域名换成裸 IP 吗?
这可能破坏 TLS 主机名验证、网关路由与运维灵活性。诊断时使用受控固定机制,同时保留预期域名。
合规说明
仅将代理用于合法、已授权的任务。遵守目标条款、robots 指示、访问控制、隐私义务和限流。不得利用 DNS 变更、备用网关或重试规避访问决定。只保留可靠性和合规所需的诊断数据。
内部研究依据:curl 项目的 DNS 缓存时间文档、Node.js DNS 文档、IETF RFC 2308 负向 DNS 缓存规范。来源 URL 仅保存在内部运营记录中。