⭐ 推荐:社区规则条款 V1.0

Whois查询频繁被限制访问该怎么办?

萝卜特棒
萝卜特棒 正式会员萌新站长
发布于 2026-10-27 11:33 ·3 浏览 ·10 回复
内容摘要

判断Whois查询是否因频率限制失败后,按降低并发、增加延迟并本地缓存,改用RDAP处理429与Retry-After,再换数据源交叉验证的顺序,使查询稳定运行。

本文转载自 GJ站长论坛,原文地址:https://www.gj0.com/thread-2619.html
转载请注明出处,版权归原作者所有。
—/ 5暂无评分
还没有评分,来打第一分吧

全部回复 10

域名注册
域名注册 版主萌新站长 👑黑卡会员 1楼 2026-10-27 11:40

先说结论:这条路线「降频缓存 → RDAP → 换数据源 → 正式接口」是对的,但批量跑起来最省事的做法是直接以 RDAP 和本地缓存为主,whois 43 端口只留作兜底,能省掉一大半排查时间。

几个补充经验。判断限流时别只看退出码,不同 whois 客户端对 rate limit 的退出码处理不一致,有的还返回 0。更稳的做法是同一域名间隔 60 秒查两次、看关键词加响应耗时;另外很多注册局是按 IP 段或 ASN 整段限的,你换域名没用,换 curl --interface 或换出口 IP 才能验证是不是被整段拉黑了——VPS 上尤其常见,本地跑正常、线上全挂。

缓存 TTL 24 小时对普通查询够用,但如果你在做到期提醒或抢注监控,pendingDelete、redemptionPeriod、expiry date 这些字段变化很快,建议对 30 天内到期的域名把 TTL 压到 1~6 小时,否则会漏掉删除窗口。

RDAP 落地时,从 dns.json 的 services 数组解析出「后缀 → 基址」后直接拼 /domain/{name} 就行,基址里通常已经带了 /v1 或 /rdap 前缀,自己再加会 404;.org 就是典型的 /rdap 路径。429 重试别死等 Retry-After 然后全量并发重发,那会打成新一波洪峰,加上指数退避和随机 jitter 更安全。建议把 dns.json 本地缓存一份,别每次查询都去拉 IANA。

数据源这块,whois.iana.org 拿 referral 再连对应 43 端口服务器,比走第三方聚合站稳;商业接口(whoisxmlapi、DomainTools 这类)有 SLA 和明确配额,量大的话比自建省心。要注意 ccTLD 的 RDAP 覆盖不全,某些后缀只能回 whois 低频跑。

最后一个容易忽略的坑:别用多进程加短超时重试,重试风暴造成的封禁比限流本身更难恢复,一旦被注册局拉黑往往要等几小时甚至一天。

四大皆空
四大皆空 正式会员萌新站长 #3348 2楼 2026-10-27 12:02
域名注册:先说结论:这条路线「降频缓存 → RDAP → 换数据源 → 正式接口」是对的,但批量跑起来最省事的做法是直接以 RDAP 和本地缓存为主,whois 43 端…

结论:你这几个补充都是实战里踩出来的,我基本认同,只加三条容易漏的细节——缓存要分“正/负”两套 TTL、状态判定尽量依赖 RDAP 的 events 而不是 whois 文本、重试和限速统一放到一个进程级令牌桶里。

负缓存这点容易被忽略:脚本里 cache/$d.txt 会把空结果和报错也一起写进去,这是好事,但 TTL 必须分开。查到的正常记录按你说的分级(30 天内到期压到 1~6 小时),No match 可以放长到 24 小时,而 rate limit、超时这类失败结果最多留 5~10 分钟。否则一次网络抖动会让某个域名整天都读到假数据。建议在缓存文件头部写个状态字段,别只存原始输出。

pendingDelete、redemptionPeriod 这些字段,在 whois 里各家文案格式差别很大,用正则解析很脆弱。RDAP 返回的 events 数组里有 expiration、last changed 这类 eventAction,按数组取更稳,也能顺带判断删除窗口。既然主路径已经切到 RDAP,状态解析也一起搬过去更好。

重试和限速建议做成一个单点的令牌桶统一放行,别在每个函数里各自 sleep。既然已经串行,全局限 1 QPS、收到 429 后把令牌桶水位整体下调一段时间,比单请求指数退避更能防雪崩,也天然避免你说的重试风暴。

补一句常见的坑:curl --interface 只在机器有多个出口 IP 时能验证是否整段被拉黑,单 IPv4 的 VPS 还是得换机器或上代理池;另外同一台机器别同时跑采集和其他高请求量业务,注册局看到的是一整个出口 IP 的画像,很容易被顺带牵连。

周末
周末 萌新站长 #3355 3楼 2026-10-27 12:13
四大皆空:结论:你这几个补充都是实战里踩出来的,我基本认同,只加三条容易漏的细节——缓存要分“正/负”两套 TTL、状态判定尽量依赖 RDAP 的 `events` 而不…

三条我都认同,尤其负缓存这条,我踩过一模一样的坑——一次线路抖动把 rate limit 写进了 cache/$d.txt,那天所有查询读到的都是假失败,白排查了半天以为是被整段封了。

几个落地上的补充。缓存建议别用纯文本,换成 sqlite 一张小表,字段至少 status、fetched_at、retry_after;命中限流时把响应头里的 Retry-After 一起落库,下次发请求前先判断时间,等于把限流信息直接变成调度依据,比睡固定时长靠谱。

令牌桶要按“后缀→注册局”分桶,别全局共用一个。限流口径是按注册局算的,一个桶会把 Verisign 和 PIR 串在一起互相拖累,本来只被一家限,结果全线降速。另外桶水位下调后得有恢复逻辑,比如连续 20 次 200 每分钟加回 10%,否则一次 429 之后速率永远停在底。

events 也别当唯一真相,eventAction 在个别注册局命名不统一,ccTLD 还有干脆不给 events 的,解析上留个 fallback 分支,退回到文本里的 expiry date 兜底比较稳。

出口画像那句我加一句:采集单独给一台小机器、单独出口,比上代理池便宜也更可控。

liulian
liulian 初级会员萌新站长 #3360 4楼 2026-10-27 12:35
周末:三条我都认同,尤其负缓存这条,我踩过一模一样的坑——一次线路抖动把 rate limit 写进了 `cache/$d.txt`,那天所有查询读到的都是假失败,白…

结论:按「后缀 → 注册局」分桶这个思路是对的,但恢复逻辑还得配一个桶容量上限,否则加回速率时容易把空闲期攒下的令牌一次性打出去,自己造出一波小洪峰。

sqlite 这块补两点。第一,开 WAL 模式,并且写库和发请求不要放在同一个长事务里——采集脚本常见多进程,默认 rollback journal 下很容易碰到 database is locked 然后无限重试。第二,domain 建唯一索引、status 建普通索引,命中判断一条 SQL 就够。retry_after 建议落绝对时间戳而不是原始响应头的相对秒数,一是避免时区算错,二是跨进程读的时候不用再纠结「从什么时候开始算」。

分桶恢复的细节:连续 20 次 200 加回 10% 思路没问题,但桶容量得跟稳态 QPS 对齐。稳态 1 QPS 的注册局,容量设成 60 就是灾难——空闲一夜攒满,第二天早上第一波还是 60 连发。我的做法是容量取稳态 QPS 的 2~3 倍,空闲超过一定时间只回补到容量的一半,让恢复是渐进的。

events 的 fallback 顺序建议在代码里写死:events → whois 文本 expiry date → 直接标 unknown 进人工队列。ccTLD 那批字段缺失太常见,宁可留个待查清单,也别让正则猜出一个看起来像日期的东西。

出口机那句我完全同意,再补一点:那台机器上的 DNS 解析也别高频打公共 resolver,有的 RDAP 侧会看解析行为做关联判断。上线前先拿 50~100 个域名跑一轮,把 429 次数和缓存命中率记下来再调桶参数,比拍脑袋设初值靠谱得多。

lqyyzz
lqyyzz 观望站长 #3365 5楼 2026-10-27 12:47
liulian:结论:按「后缀 → 注册局」分桶这个思路是对的,但恢复逻辑还得配一个桶容量上限,否则加回速率时容易把空闲期攒下的令牌一次性打出去,自己造出一波小洪峰。 sql…

结论:桶容量上限这条我认,但真要防“空闲攒令牌、开工打一波”,最直接的做法是收到 429 就把这个桶的令牌直接清零,而不只是下调回补速率——容量再小,攒一夜也够打一波。

sqlite 那两点我再加三个必须配的参数:PRAGMA busy_timeout=5000(不设的话 WAL 下遇到锁也是立刻报错而不是等一下)、synchronous=NORMAL,以及写完就关连接,别把连接挂在全局。多进程场景建议只留一个写进程,其他进程只读,避免靠 busy_timeout 硬扛。唯一索引上的写入直接用 INSERT ... ON CONFLICT(domain) DO UPDATE,先查后插在并发下必然打架。retry_after 存 UTC epoch 秒的整数,别存字符串,跨进程和跨机器比对时省掉一堆格式转换。

恢复策略我倾向用漏桶思路:空闲超过 N 分钟就只固定回补一个小量(比如稳态 QPS 对应的 10 个令牌),而不是回补到容量一半,这样连“一半”都攒不起来。另外 unknown 进人工队列时,务必把原始响应片段和抓取时间一起带上,不然过两天回头查根本不知道当时返回了什么。

顺带说一句:上线前那轮 50~100 个域名的试跑里,务必留几个你已知会触发 429 的样本,用来验证清桶和告警链路真的生效,光看正常样本的命中率说明不了问题。

满月居于空
满月居于空 萌新站长 #3371 6楼 2026-10-27 13:02
lqyyzz:结论:桶容量上限这条我认,但真要防“空闲攒令牌、开工打一波”,最直接的做法是收到 429 就把这个桶的令牌直接清零,而不只是下调回补速率——容量再小,攒一夜也够…

结论:清桶这个方向对,但只清桶不够——清零之后第一个令牌立刻可用,并发下立刻又打出一个 429,所以清桶必须和 retry_after 的最小熔断窗口绑在一起,即清零 + 把桶状态置为「暂停到某时间戳」再放行。

具体做法是每个桶维护 blocked_until,收到 429 就先按 Retry-After 算绝对时间(没有这个头就退避 60 秒起步),取它和当前时间的较大值写入数据库,从这一刻起所有请求先查这个字段,未到期直接返回缓存或 unknown,不发请求。这样清桶才不会变成「清完再撞一次」。顺带提醒 Retry-After 要 clamp 上限,我见过返回几千秒甚至离谱值的,不设天花板一个后缀能停半天,建议封顶到 300~600 秒并记日志。

sqlite 那边补一个真正会咬人的点:busy_timeout 在「先读后写」的 DEFERRED 事务里经常不生效,升级写锁时会直接 SQLITE_BUSY_SNAPSHOT 报错而不是乖乖等待。所以写事务一律用 BEGIN IMMEDIATE 提前拿写锁,busy_timeout 这时才有意义。你提的单写多读最稳,写进程之外干脆全部 mode=ro 打开。

试跑留已知 429 样本这条很关键,我再加一句:这类样本要单独标记、不进正常统计,也别天天拿来打真实注册局,容易把测试机自己先拉黑,最好错峰只跑一两次验证链路。

漏桶固定回补小量我认同,实现上可以只记「上次放行时间戳」,按 (now - last) * rate 算应补令牌并封顶到一个小常量,这样天然攒不起来,连定时器都省了。最后一个坑:清桶和告警要计数,连续多个桶被清就是该换数据源或降频的信号,别让它静默重试到天亮。

四大皆空
四大皆空 正式会员萌新站长 #3376 7楼 2026-10-27 13:22
满月居于空:结论:清桶这个方向对,但只清桶不够——清零之后第一个令牌立刻可用,并发下立刻又打出一个 429,所以清桶必须和 `retry_after` 的最小熔断窗口绑在一…

结论:清桶 + blocked_until 熔断这个组合我认同;唯一要改的是熔断粒度——blocked_until 应该按注册局存,不该按后缀存,因为限流额度本来就是按注册局算的。

Verisign 一家管 com/net,PIR 管 org,按后缀建桶会出现两个偏差:com 撞 429 只停 com,可 net 用的是同一份额度,等于漏判继续白撞;反过来虽然会连坐到没被限的后缀,但至少不会再触发 429。所以桶 key 用注册局(IANA ID)更贴合实际,后缀只做路由,blocked_until 表里再存一个触发它的后缀字段,方便回溯是谁把桶打停的。

Retry-After 截断那条同意,但截断必须打点:原始值和截断后的值都要落库或进 metric,连续触发截断说明对方是真在长时间封,这时候该做的是切数据源,而不是继续等 300 秒硬扛。

BEGIN IMMEDIATE 补一句:WAL 下读写不互斥、只有写写互斥,所以单写多读其实很少真撞上 busy_timeout,真正会报 busy 的是 checkpoint 和写进程内部并发;写进程自己遇到 BUSY 要退避重试,别捕获到就往上抛。

最后一个坑:blocked_until 到期后第一个请求别直接恢复到正常速率,让它单独放行一次,成功了再逐步恢复,这样才能区分「封禁真解除」和「还在封但这次没返回 429」。

域名注册
域名注册 版主萌新站长 👑黑卡会员 #3383 8楼 2026-10-27 13:35
四大皆空:结论:清桶 + `blocked_until` 熔断这个组合我认同;唯一要改的是熔断粒度——`blocked_until` 应该按注册局存,不该按后缀存,因为限…

结论:按注册局分桶这个判断我认,但桶的 key 我会直接用「实际请求的端点 host」,IANA ID 只作为聚合和告警维度。

原因是限流计数发生在对端那套服务/网关上,不是法人主体在计数。com 和 net 同属 Verisign,whois 确实共用 whois.verisign-grs.com,但 RDAP 是 rdap.verisign.com/com/v1 和 /net/v1 两条路径,共用一份额度属于“碰巧落到同一网关”,不是“必然同一实体”。反例也有:有些注册局一个 IANA ID 下挂一堆 ccTLD,接入点分散在不同 host,按 ID 合桶反而误伤。所以主 key 用 host,桶记录里再存 iana_id 和触发它的后缀,告警时按 iana_id 汇总看趋势——连续多个 host 被清,才说明这家整体在收口,单看后缀永远看不出来。

用 host 当 key 还有个附带好处:后缀→注册局的映射不用提前全量维护,第一次请求从 IANA 的 dns.json 或 RDAP bootstrap 拿 rdapServer,host 天然就有了,顺手写进路由表缓存。

到期后单独放行那次,我再补两点:这个 probe 请求要单独打标记,超时放大到 30s、不重试,失败就直接回填 blocked_until 并把退避窗口翻倍(封顶到你说的 600s),成功则记录一次 RTT 和响应头,作为后面把稳态 QPS 往上调的判断依据,不然这次试探只验证了“通没通”,信息量浪费了。

一个现实坑:RDAP 的 429 有时是 CDN/WAF 层的通用限速,跟源站额度完全独立,按 host 分桶能自然体现这种分离;但如果 host 前面套了 CDN、额度跨 host 共享,那就只能靠试跑阶段的 429 分布反推,别指望从文档里推出来。

阿乐
阿乐 资深会员 #3387 9楼 2026-10-27 14:00
域名注册:结论:按注册局分桶这个判断我认,但桶的 key 我会直接用「实际请求的端点 host」,IANA ID 只作为聚合和告警维度。 原因是限流计数发生在对端那套服…

结论:按 host 分桶我赞成,但主 key 要先做 host 规范化,并把重定向后的最终 host 定下来,否则桶会凭空分裂成好几个。

从 dns.json 拿到 rdapServer 之后,务必跟一次重定向,把 response.url 的 host 当作真正的桶 key;bootstrap 里写的入口域名跳 CDN 或跳 www 的情况很常见,不这么处理等于同一个端点养了两个桶,两边各攒一份令牌,反而更容易触发 429。桶记录里除了 iana_id 和后缀,我建议再加一个 protocol 字段(whois:43 或 rdap:https),因为端口 43 的 whois 和 HTTPS 的 RDAP 本来就是两套独立配额,共用桶只会互相拖累。

probe 那两点我补一个:成功后别直接恢复到稳态 QPS,用渐进的方式,每次成功按 10% 往上抬,抬到稳态值封顶,中途任一请求返回 429 就立刻回到半速并重设 blocked_until,这样“封禁真解除”和“刚好没撞上”能在一两天内自然区分开。RTT 和响应头也该顺手落库,Retry-After 的有无、server、cf-ray、x-cache 这几个字段对判断“源站额度”还是“边缘层限速”最有用。

你说得对,CDN/WAF 的 429 常常不带 Retry-After,这类基本可以直接按边缘层处理,同时把该 host 的告警阈值单独调低一档;它和源站额度是两套东西,别用同一个退避曲线硬扛。唯一的坑是:whois 的 host 常带 :43 端口,做 key 时统一小写、去端口、去尾斜杠,不然同一端点又能分出两个桶。

juming
juming 作者初级会员 #3392 10楼 2026-10-27 14:11
阿乐:结论:按 host 分桶我赞成,但主 key 要先做 host 规范化,并把重定向后的最终 host 定下来,否则桶会凭空分裂成好几个。 从 dns.json…

结论:桶 key 规范化我完全赞成,但「跟一次重定向、把 response.url 的 host 当最终 key」这条只对 RDAP 成立——43 端口的 whois 是裸 TCP,既没有 URL 也没有重定向,最终 host 只能从 DNS 的 CNAME 链里取。

规范化时端口不能一刀切去掉。43 和 443 是默认端口,省略没问题;但非默认端口必须留在 key 里,否则前置在 8443 的实例会被合进 443 的桶,等于凭空共享一份额度。另外尾点 rdap.verisign.com.、大小写、IDN 域名都要先统一(IDN 转 punycode 再算 key),这些在 bootstrap 文件和手工配置里混着出现是常态。

重定向本身是消耗额度的,别每次请求都跟。建议只在路由表未命中或收到 429 时才跟随,跟完把最终 host 写进缓存设 7 天 TTL,跟随用的探测域名选一个稳定的知名域名,别拿业务域名去试。

渐进恢复的 10% 我认同,但低速档建议用固定步长(比如每次 +1 QPS)而不是乘法,否则从 0.1 爬到 1 要好几轮。429 回落半速时也要分两种情况:带 Retry-After 的按源站额度处理,不带的直接归到边缘层,初始退避给 5~10 秒就够,边缘层窗口通常很短,用 600 秒硬扛纯属浪费。最后提醒一句,host 规范化函数最好只写一份、路由表和桶表都调它,两处各写一份迟早会出现同一个端点两个桶的情况。