你的识别请求为什么偶尔多等 200 毫秒,其余时候却很快?八成是 DNS。
每次调用 ocr.captchaai.com,系统都可能重新解析域名,耗时 5–200 毫秒。本文讲清楚 DNS 何时是真瓶颈,怎么用几行代码消除它。
什么情况下 DNS 才算真正的瓶颈
- 每个请求都新建连接 — 没用
Session(Python)或 keep-alive agent(Node.js) - 容器或 serverless 冷启动 — 新实例没有缓存 DNS 记录
- DNS 服务商响应慢 — 用的是没有本地缓存的运营商默认 DNS
- 大批量并发识别 — 大量 worker 同时启动,集中发起解析
关键结论: 用了 HTTP keep-alive,DNS 基本不用操心——同一个 TCP 连接会复用已解析的 IP。只有每次都新建连接,DNS 才会真正拖慢耗时。
单次识别请求,DNS 到底占多少毫秒
一次完整识别通常包含 5–7 个 HTTP 请求(1 次提交 + 4–6 次轮询)。没有 DNS 缓存时:
| 场景 | DNS 查询次数 | 增加的延迟 |
|---|---|---|
| 无缓存,DNS 响应慢(每次 200 毫秒) | 7 | 1,400 毫秒 |
| 操作系统级 DNS 缓存(仅首次调用) | 1 | 200 毫秒 |
| 使用长连接(0 次新查询) | 0 | 0 毫秒 |
| DNS 预解析 + 长连接 | 0 | 0 毫秒 |
Python:把 DNS 延迟降到 0
先测量,再决定要不要优化
import socket
import time
# Measure DNS resolution time
hostname = "ocr.captchaai.com"
start = time.time()
ip = socket.getaddrinfo(hostname, 443)
first_resolve = time.time() - start
start = time.time()
ip = socket.getaddrinfo(hostname, 443)
second_resolve = time.time() - start
print(f"First resolve: {first_resolve*1000:.1f}ms")
print(f"Second resolve: {second_resolve*1000:.1f}ms (OS cached)")
下面代码先手动解析一次 IP,再用 requests.Session 开启 keep-alive——真正省下后续 DNS 查询的是 Session 的连接复用。
import os
import socket
import requests
from urllib3.util.connection import create_connection
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
# Pre-resolve the API hostname
CAPTCHAAI_IP = socket.getaddrinfo("ocr.captchaai.com", 443)[0][4][0]
print(f"Resolved ocr.captchaai.com to {CAPTCHAAI_IP}")
# Patch connection to use cached IP
DNS_CACHE = {"ocr.captchaai.com": CAPTCHAAI_IP}
class CachedHTTPAdapter(requests.adapters.HTTPAdapter):
def send(self, request, **kwargs):
return super().send(request, **kwargs)
# Use with Session for fastest resolution
session = requests.Session()
session.headers.update({"Connection": "keep-alive"})
# The session already maintains keep-alive, so DNS is resolved once
# For the first request, the OS cache handles subsequent lookups
resp = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance", "json": "1",
})
print(f"Balance: {resp.json()}")
换一个更快的 DNS 解析器
能控制 DNS 配置的话,直接换成响应更快的公共 DNS:
# For systems where you control DNS configuration:
# /etc/resolv.conf (Linux) or system DNS settings
# Recommended: Cloudflare (1.1.1.1) or Google (8.8.8.8)
# In Python, you can also use dnspython for explicit resolution
import dns.resolver
resolver = dns.resolver.Resolver()
resolver.nameservers = ["1.1.1.1", "8.8.8.8"]
answers = resolver.resolve("ocr.captchaai.com", "A")
for answer in answers:
print(f"Resolved: {answer}")
Node.js:把 DNS 延迟降到 0
测量并复用连接
const dns = require('dns');
const { performance } = require('perf_hooks');
const hostname = 'ocr.captchaai.com';
// First resolution
const start1 = performance.now();
dns.lookup(hostname, (err, address) => {
const time1 = performance.now() - start1;
console.log(`First resolve: ${time1.toFixed(1)}ms → ${address}`);
// Second resolution (OS cached)
const start2 = performance.now();
dns.lookup(hostname, (err2, address2) => {
const time2 = performance.now() - start2;
console.log(`Second resolve: ${time2.toFixed(1)}ms → ${address2}`);
});
});
用 keep-alive agent 之后,一个连接生命周期内只解析一次 DNS:
const dns = require('dns');
const https = require('https');
const axios = require('axios');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
// Pre-resolve and cache
let cachedIP = null;
async function preResolve() {
return new Promise((resolve, reject) => {
dns.lookup('ocr.captchaai.com', (err, address) => {
if (err) reject(err);
cachedIP = address;
console.log(`Cached IP: ${cachedIP}`);
resolve(address);
});
});
}
// Use keep-alive agent (DNS resolved once per connection)
const agent = new https.Agent({
keepAlive: true,
maxSockets: 20,
keepAliveMsecs: 60000,
});
const api = axios.create({
baseURL: 'https://ocr.captchaai.com',
httpsAgent: agent,
timeout: 30000,
});
(async () => {
await preResolve();
const resp = await api.get('/res.php', {
params: { key: API_KEY, action: 'getbalance', json: '1' },
});
console.log(`Balance: ${resp.data}`);
})();
Serverless 和容器环境里的 DNS 陷阱
AWS Lambda、Google Cloud Functions 和 Docker 容器里,DNS 缓存行为并不一样:
- AWS Lambda — 缓存在执行上下文里,冷启动后失效;在 handler 初始化阶段预解析
- Google Cloud Functions — 缓存在实例内部;在全局作用域预解析
- Docker — 默认使用宿主机 DNS;配置
--dns 1.1.1.1 - Kubernetes — CoreDNS,缓存可配置;在 Pod DNS 配置里设置
ndots: 1
国内网络环境下,DNS 延迟经常被高估
从国内网络访问海外域名,DNS 确实会更慢,但开销未必来自 DNS 本身:
- 用
dig或nslookup单测一次解析耗时,和整段请求对比 - 通常会发现解析只占个位数毫秒,真正拖慢的是往返时延
- 脚本跑在海外云主机上,差异明显更小
- 先测量、再决定要不要换 DNS
故障排查对照表
遇到下面情况,直接对照处理:
| 现象 | 原因 | 处理方式 |
|---|---|---|
| 首次 API 调用慢,其余请求都快 | 首次调用要做一次 DNS 查询 | 属于正常的系统缓存行为,配合 keep-alive 使用即可 |
| 所有请求都慢(额外增加 100 毫秒以上) | 没有 DNS 缓存,解析器响应慢 | 把 DNS 换成 1.1.1.1 或 8.8.8.8 |
| 延迟随机出现尖峰 | DNS 缓存 TTL 到期 | 延长本地缓存 TTL,或改用预解析 |
| 容器冷启动慢 | 新实例上没有缓存的 DNS 记录 | 在初始化代码里做预解析 |
常见问题
已经用了 keep-alive,还需要额外做 DNS 优化吗?
大多数情况下不需要,长连接下 DNS 只解析一次;真正值得优化的是冷启动和每个请求都新建连接的场景。
为什么本地开发很快,部署到线上就变慢?
本地 DNS 记录通常已被系统缓存,线上环境(尤其 serverless)冷启动要重新解析。
CaptchaAI 的 IP 会变化吗,会不会影响长连接?
- 会变化,这是正常的负载均衡
- 长连接固定使用建连时解析到的 IP,不受后续 DNS 变化影响
怎么快速确认延迟是不是 DNS 导致的?
对比首次解析、二次解析(系统缓存)和整个请求的耗时,三者接近时,问题基本不在 DNS。
下一步
把这些方法用到你的识别管道里——立即获取你的 CaptchaAI API 密钥,再看看下面这几篇相关指南: