本套方案为设计,供参考。暂无时间实践操作,后续实践一番。
建议:无法备案用户使用。若备案,直接买国内CDN即可,无需如此麻烦。
建议:用户足够多的时候才采用,用户量小时无需为此一顿操作。
一、问题背景:为什么"多域名 ≠ 自动选择最快"
很多站长都会遇到类似场景:
-
有 多个域名(A / B / C / D)
-
分别部署在 不同服务器 / 不同地区
-
都能访问同一套后端服务
-
希望实现:
- 用户访问自动选择最快的域名
- 某个域名或服务器异常时,用户无感知切换
- 不依赖昂贵的全局 CDN / Anycast
但现实是:
- DNS 不能判断"哪个域名对某个用户最快"
- DNS 只能在 IP 层 做多地址返回与简单健康检查
- 浏览器也不会替你比较多个域名
结论一句话:
"多域名本身,不具备自动择优能力。"
要实现这个目标,必须在浏览器侧引入逻辑。
二、核心思想:把"最快"的判断权交给用户浏览器
这套方案的核心思想非常简单,也符合真实的网络工程逻辑:
谁离用户最近、网络路径最好,
就由谁(用户浏览器)来判断。
因此:
- 不在服务器判断
- 不在 DNS 判断
- 而是在 用户真正发请求的那一刻判断
我们让浏览器做三件事:
- 同时请求多个候选域名
- 记录真实请求耗时(非 ping)
- 选择最快且可用的那个域名,并跳转
三、整体架构说明
这里为有四个入口域名:
- A:
https://share.zhangsan.cool - B:
https://share-hk.zhangsan.cool - C:
https://share.searchknowledge.cloud - D:
https://hello.aiforme.cloud
每个域名:
- 各自解析到一台服务器
- 各自可以独立访问
- 各自反向代理同一个真实后端服务
E
整体结构:
用户浏览器
↓
任意域名 A / B / C / D
↓
页面加载时:浏览器测速 A/B/C/D
↓
选择最快且可用的域名
↓
自动跳转 + 缓存结果
一个非常重要的现实前提
⚠️ 如果用户访问的那个域名 完全打不开(被墙 / 被 DNS 污染) ,
浏览器 连页面都拿不到,自然无法执行自动逻辑。
因此:
- 至少要 有一个域名能打开首页
- 或用户知道其它备用域名
这是任何自动方案都无法绕过的物理限制。
四、DNS 层配置原则(以腾讯云 DNSPod 为例)
1️⃣ 每个域名互相独立(不要 CNAME 依赖)
错误做法(风险极大):
B / C / D → CNAME → A
一旦 A 域名出问题,全部都会"连坐"。
✅ 正确做法:
A / B / C / D 都直接使用 A 记录,各自独立解析
示例配置(每个域名各自配置)
以 share.zhangsan.cool 为例:
| 主机记录 | 类型 | 记录值 |
|---|---|---|
| share | A | IP1 |
其他域名同理:
share-hk.zhangsan.cool→ IP2share.searchknowledge.cloud→ IP3hello.aiforme.cloud→ IP4
这样做的结果是:
- 任意一个域名异常,不影响其他域名
- 域名层面做到真正解耦
五、服务器侧:提供统一的健康测速接口 /ping
浏览器要测速,必须有一个极轻量、稳定、永远快速返回的接口。
推荐做法:Nginx 直接返回
在每个站点对应的 Nginx 中加入:
nginx
location = /ping {
add_header Content-Type application/json;
return 200 '{"status":"ok"}';
}
特点:
-
不走后端业务
-
不消耗资源
-
即使后端挂了,也能区分出:
- "前端节点还活着"
- vs "整个站都死了"
六、关键部分:浏览器测速与自动跳转逻辑
这是 "自动选择最快域名" 的核心。
设计原则
- 并发测速,避免串行浪费时间
- 超时即失败,避免最差线路拖慢整体
- 结果缓存,避免每次页面打开都测速
- 只在必要时跳转,避免无限循环
可直接使用的前端脚本
把下面这段代码,原样复制到四个站点的公共页面中即可:
html
<script>
(function () {
const MIRRORS = [
"https://share.zhangsan.cool",
"https://share-hk.zhangsan.cool",
"https://share.searchknowledge.cloud",
"https://hello.aiforme.cloud"
];
const CACHE_KEY = "best_mirror_cache";
const CACHE_TTL = 10 * 60 * 1000; // 10分钟
const TIMEOUT = 2000; // 单个测速超时:2秒
const current = location.origin;
// 1. 读取缓存
try {
const cached = JSON.parse(localStorage.getItem(CACHE_KEY));
if (cached && Date.now() - cached.time < CACHE_TTL) {
if (cached.origin !== current) {
location.replace(cached.origin + location.pathname + location.search);
}
return;
}
} catch (e) {}
// 2. 测速函数
function ping(origin) {
return new Promise(resolve => {
const start = performance.now();
const controller = new AbortController();
const timer = setTimeout(() => {
controller.abort();
resolve({ origin, ok: false, cost: Infinity });
}, TIMEOUT);
fetch(origin + "/ping?_=" + Math.random(), {
signal: controller.signal,
cache: "no-cache"
})
.then(r => {
clearTimeout(timer);
resolve({
origin,
ok: r.ok,
cost: r.ok ? performance.now() - start : Infinity
});
})
.catch(() => {
clearTimeout(timer);
resolve({ origin, ok: false, cost: Infinity });
});
});
}
// 3. 并发测速并择优
window.addEventListener("load", async () => {
const results = await Promise.all(MIRRORS.map(ping));
const alive = results.filter(r => r.ok);
if (!alive.length) return;
alive.sort((a, b) => a.cost - b.cost);
const best = alive[0];
localStorage.setItem(CACHE_KEY, JSON.stringify({
origin: best.origin,
time: Date.now()
}));
if (best.origin !== current) {
location.replace(best.origin + location.pathname + location.search);
}
});
})();
</script>
七、这套方案是如何"判断最快"的?
很多人关心这点,这里说清楚。
实际判断过程是:
-
浏览器真实建立 HTTP 连接
-
包含:
- DNS 查询
- TCP 三次握手
- TLS 握手
- 实际 HTTP 响应
-
真实耗时 =
performance.now()差值
这比:
- ping
- 人为指定线路
- 后端猜测
都更接近用户真实体验。
八、缓存与体验优化
为什么要用 localStorage?
- 假如用户已经测出最快是 B
- 下一次再访问任意域名
- 直接跳过去,不再测速
- 10 分钟后自动重新评估
这样做到:
- 页面不闪
- 不频繁跳转
- 网络变化时可自动更新
九、方案边界与真实限制
这套方案 不是万能的,但它是"现实可用"的。
✅ 它能做到
- 多域名真正自动择优
- 某个域名异常时自动绕开
- 不依赖 CDN / LB
- 实现成本极低
❌ 它无法做到
- 用户只知道 A,而 A 被彻底封锁 → 自动跳 B
(因为 JS 没机会执行) - 毫秒级全局最优调度
- 替代专业 Anycast / 全球 LB
实际运营建议
始终公开所有可用域名列表
并明确告知用户:
"任一入口均等价,系统会自动选择最快线路。"
十、总结:这是"工程上合理"的最佳解
一句话总结这套技术选择:
DNS 负责"能不能到",
浏览器负责"快不快",
缓存负责"不折腾用户"。
对于个人站长、中小团队:
- 成本极低
- 逻辑清晰
- 可维护性强
- 不容易翻车
这就是这套方案最大的价值。