尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

DNS负载均衡不会自动避开故障?用健康检查+动态摘除实现高可用

发布时间:2026/9/24 18:57:33

资讯中心
01
ARTICLE

DNS负载均衡不会自动避开故障?用健康检查+动态摘除实现高可用

DNS负载均衡不会自动避开故障?用健康检查+动态摘除实现高可用
“DNS负载均衡能自动避开故障服务器吗”这个问题我在不同的技术群里见过不下十次。问的人多了说明这块的误解确实普遍很多人默认DNS负载均衡是一种“智能调度器”能感知后端故障并自动切换。但实际上纯DNS解析流程里根本没有“健康检查”这一步它只负责把IP地址返回给客户端至于这个IP背后是活蹦乱跳的服务器还是一台已断电的机器DNS服务器一概不知。这篇文章就把这件事彻底拆开讲清楚顺便给出一套能实现的方案和踩坑记录适合运维、后端开发以及所有打算用DNS做流量调度的人参考。1. 先说结论DNS负载均衡本身不会自动避开故障1.1 三种常见的DNS负载均衡实现方式要理解“能不能自动避开”先得知道现在常见的DNS负载均衡是怎么做的。我按实际使用频率排一下普通轮询(Round-Robin)这是最典型的实现。在DNS服务器上为同一个域名配置多条A记录例如demo.example.com分别指向203.0.113.10、203.0.113.11、203.0.113.12DNS服务器在收到查询请求时轮流返回这些IP。这种方式的优点是零成本、配置简单缺点是它完全不关心服务器的实际状态。加权轮询(Weighted Round-Robin)给不同的A记录配置不同的权重比如让性能好的机器多分点流量性能差的少分点。但本质上和普通轮询一样只是告诉DNS服务器“返回某个IP的概率更高”依然没有故障感知能力。地理/智能DNS根据客户端IP的归属地或者运营商返回距离最近、链路最好的节点IP。这个比前两者“聪明”一点但它优化的是“解析结果”本身而不是“后端存活状态”。这三种方式共同的问题都是DNS服务器只做“域名到IP的映射”不做“IP背后服务的健康探测”。换句话说DNS负载均衡解决的是“流量怎么分散到多台机器上”而不是“哪台机器还活着”。1.2 “自动避开”在纯DNS流程里不存在我用一个场景来说明。假设你有两台Web服务器一台在上海IP A一台在北京IP B域名做了轮询。某天上海机房断电IP A已经彻底不可达。此时用户发起域名解析请求本地DNS缓存没有记录就去权威DNS查询。权威DNS并不知道上海机房断电了它按照轮询规则返回IP A给用户。用户的浏览器拿着IP A去建立TCP连接连接超时页面加载失败。整个过程里DNS服务器做出的行为是“按规则返回IP”没有任何一个环节去检查IP A是否可达。所以纯DNS负载均衡不仅不能自动避开故障甚至在故障发生时还会把一部分流量“送”到故障服务器上导致这部分用户出现访问失败。这不是DNS的缺陷而是它的设计定位决定的解析就是解析连接就是连接两者之间没有反馈回路。2. 为什么DNS感知不到服务器挂了解析与连接之间的认知断层2.1 解析和连接是两段独立的过程很多人把“DNS解析”和“HTTP请求”当成一个整体潜意识里觉得“我访问某个域名浏览器先去问DNSDNS返回后浏览器再连接如果连接不上DNS应该也能知道吧”——这个直觉在工程上不成立。DNS解析走的是UDP/TCP 53端口它是一次独立的请求-响应过程。客户端问demo.example.com的IP是什么DNS服务器查自己的记录表把结果返回然后这次解析就结束了。至于客户端拿到IP之后是用80端口去连Web服务还是用22端口去连SSH还是根本没发起任何连接DNS服务器完全不知道。更关键的是参与解析的还有中间层。一次完整的DNS查询往往要经过客户端 → 本地递归DNS通常是运营商或企业的DNS服务器→ 根域/顶级域 → 权威DNS服务器层层缓存。每一层都只做“查询-转发-缓存”的工作没有任何一层会去探测“这个域名解析出来的IP端口是否开放、服务是否正常”。2.2 TTL缓存让切换无法立即生效再换个角度。就算你有办法让DNS服务器感知到某一台后端故障并立刻修改解析结果这个修改也未必能马上到达用户。因为DNS响应里带有TTLTime To Live生存时间它告诉各个缓存层“这条记录你可以缓存多久”。假设TTL设置为300秒5分钟那么故障发生前用户的本地DNS服务器可能已经缓存了旧的解析结果。即使权威DNS你已经把故障IP摘除了缓存里的旧记录依然会被返回给用户最长可能持续5分钟。这意味着DNS层面的故障切换天然存在“TTL级”的延迟。TTL设得越短切换越迅速但查询频率越高权威DNS压力越大TTL设得越长切换越慢但缓存命中率高解析性能好。2.3 客户端重试策略是唯一的“伪避障”兜底那么问题来了既然DNS自己不会避障为什么有些场景下用户刷新一下页面就好了这其实不是DNS的功劳而是客户端浏览器、操作系统、应用框架自己的重试机制在起作用。最典型的是浏览器。当浏览器拿到解析结果列表后如果第一个IP连接失败它不会直接把错误抛给用户而是会尝试列表里的下一个IP。Java的InetAddress、许多HTTP客户端库比如Apache HttpClient、OkHttp也有类似的IP failover机制。换句话说多A记录配合客户端重试能实现一种“自动避开”但这是客户端行为不是DNS行为。这里有个很重要的差异如果解析结果只有一条A记录客户端没有其他IP可以重试那就只能报错如果有两条或更多A记录客户端的重试才能生效。所以就算不引入任何健康检查机制保留多个A记录本身就能提高一定的容错性只是这种容错是概率性的而且依赖端上行为没法保证每个客户端都那么“聪明”。3. 想让DNS“自动避障”得靠外围机制补健康检查3.1 核心思路健康检查 动态更新解析记录既然DNS本身不具备健康检查能力那我们就在它外面套一层“健康检查动态更新”的机制让它间接具备自动避障能力。整体思路是这样的建立一套监控程序定时探测后端服务器的健康状态探测手段可以是TCP端口检查、HTTP请求、ICMP Ping等。当发现某个IP连续多次探测失败后监控程序通过DNS管理API把该IP从域名的解析记录中摘除或者把它标记为不可用。当故障恢复后监控程序再通过API把该IP加回解析记录。这个方案的本质是把“判断故障”的逻辑从DNS服务器里抽离出来放到独立的健康检查系统中再利用DNS动态更新的能力去修改解析结果。它虽然不是“DNS原生支持”但工程上完全可行也是很多大型网站“多活”架构的常见做法。3.2 方案A云解析服务商的智能解析/DNS Failover如果你不想自己造轮子直接用云解析服务商的现成能力是最快的。目前主流的云解析服务商比如阿里云解析、腾讯云DNSPod、AWS Route 53都支持健康检查和故障转移Failover功能。以AWS Route 53为例它的“Failover routing policy”允许你为主记录和备份记录分别设置健康检查。Route 53会定期从全球多个节点发起健康检查请求如果主记录的健康检查失败它就会在DNS响应中返回备份记录。阿里云解析的“智能解析” “健康检查”也有类似功能能自动暂停故障IP的记录返回。这类方案的好处是探测节点分布广、稳定性高、不用自己维护监控系统。坏处是要在服务商的控制台里配置逻辑相对固定在服务商提供的模式里灵活度有限而且健康检查的粒度、频率也可能受到服务商限制。3.3 方案B自建脚本 DNS服务商API如果你需要更灵活的控制或者你的DNS服务器是自己用BIND等软件搭建的那就可以走自建脚本这条路。核心步骤是写一个健康检查脚本定期去探测后端IP的指定端口或HTTP接口。脚本判断出某个IP异常后调用DNS服务商提供的OpenAPI修改域名的解析记录——把这个IP从记录列表里移除。恢复后再用相同API把IP加回来。可选把脚本做成守护进程配合日志和告警方便追踪每次切换事件。自建脚本的好处是逻辑完全可控想用TCP探测、HTTP探测还是自定义探测都随你。坏处是必须处理各种边界情况比如API调用失败、探测目标误报、并发切换冲突等对开发运维能力有一定要求。3.4 什么情况下选哪种我自己的判断标准很简单如果域名解析托管在云厂商而且要求不高优先用云解析自带的能力省心、稳定。如果域名是自建DNS或者对健康检查逻辑有特别要求那就自建脚本配合服务商的开放API来做。实际上很多公司会混搭核心域名用云解析的Failover能力边缘或测试域名用脚本方案。有一个细节值得提健康检查程序本身不能成为单点。如果你只有一台监控机它挂了DNS记录的自动更新也就停了。条件允许的话最好部署两台监控节点分别负责探测和主备切换或者至少让监控程序具备故障恢复后的“兜底拉回”能力。4. 这项组合拳能避开哪些故障又避不开哪些故障4.1 能处理的故障场景加了健康检查动态更新之后DNS负载均衡的避障能力会明显提升。它能处理以下几类故障机房断电或物理网络中断整个后端IP彻底不可达TCP探测必然失败监控程序能很快发现并摘除记录。服务器宕机、操作系统崩溃类似的端口探测会超时或拒绝连接。Web服务进程挂掉如果做的是HTTP探测服务进程异常会反映在HTTP状态码上比如5xx、超时。部分区域网络异常如果监控点分布在多个地域还能发现区域性的网络质量问题及时把流量切到其他区域。在这些场景下DNS层面的自动摘除能显著降低故障影响面。举例来说你有两台服务器做轮询一台挂了配置好健康检查摘除后新发起的DNS查询只会返回健康的那台IP。只要客户端缓存刷新够快用户影响就会被控制在极小的范围内。4.2 处理不了的故障场景但也有一些场景DNS这套组合拳是无能为力的这是我特别想强调的客户端侧缓存未过期运营商Local DNS或者用户电脑的DNS缓存可能还保存着旧记录。无论如何你都没法强制清掉所有缓存只能等TTL过期。这是行业通病也是不可突破的物理边界。假死状态进程还在端口也能通TCP层一切正常但业务已经没法处理请求了比如死锁、内存泄漏导致的缓慢、数据库连接池耗尽。这种“应用活着但服务不正常”的状态DNS探测很难准确判断因为从外部看连接是能建立的。防火墙只放行特定来源IP如果后端服务器配置了严格的安全组只允许某些来源IP访问而健康检查节点的IP不在白名单里那么健康检查会误报“故障”。连接被中间网络丢弃比如光缆被挖断但交换机还有电TCP SYN包发出去石沉大海这种故障健康检查确实能发现但只要故障链路上还有Local DNS的缓存解析结果依然可能是旧IP。理解边界很重要。DNS层面的自动避障解决的是“整个IP不可用”级别的故障而不是“服务粒度”的故障。想要更细粒度的自动恢复必须在负载均衡器或应用框架层面做文章。4.3 与nginx、LVS等负载均衡的配合思路聊到这里很多读者应该已经意识到DNS负载均衡和nginx、LVS这类负载均衡其实是两个层次的东西。DNS负载均衡是在“域名解析层”做宏观流量调度决定“用户会去哪个机房/哪片集群”。nginx、LVS是在“接入层/网络层”做微观流量分发决定“请求具体落在集群里的哪台机器”。在实际架构中两者通常是配合使用的。最常见的方式是DNS层用多个IP对应多个机房入口每个机房入口后面再挂一台nginx或LVSnginx自己再配置upstream做后端健康检查、自动摘除故障节点。这样一来DNS负责机房级的容灾nginx负责服务器级的容灾两者各司其职。nginx的负载均衡本身就支持max_fails和fail_timeout参数能自动把连续失败的后端标记为不可用并且在一段时间后重新探测恢复。这是应用层负载均衡的天然优势它能看到实际请求的成功与失败比DNS这种纯解析层的感知能力强得多。所以我的建议是别指望DNS做太细的故障转移那是负载均衡器该干的事。DNS顶多帮你把流量引到正确的入口尤其是引入云负载均衡产品后故障转移的活交给SLB/CLB这类产品效果会好很多。5. 实操用DNSPod API实现一个极简DNS故障自动摘除5.1 前置条件与设计思路下面我用一个具体的例子演示自建健康检查解析记录动态更新怎么落地。我选DNSPod作为示例因为它API接入简单文档也比较清晰。腾讯云的DNSPod也兼容同样的API体系。前置条件一个托管在DNSPod的域名比如demo.example.com。该域名配置了两条A记录分别指向203.0.113.10主、203.0.113.11备。有一台Linux机器用来跑健康检查脚本这台机器需要有公网访问能力并且能访问DNSPod的API接口。准备好DNSPod的API Token一般是通过DNSPod控制台创建格式是ID,Token。设计思路脚本每30秒循环一次分别对两条A记录的目标IP发起HTTP探测探测地址建议用http://IP/healthz这种轻量接口返回2xx就算健康。如果某个IP连续3次探测失败就调用DNSPod API把对应的记录状态改为“暂停pause”或者干脆删除记录我建议用“暂停”而不是删除这样恢复时只需要改回“启用”不用重建记录。如果某个IP恢复正常连续3次探测成功就把记录重新启用。每次切换都写日志方便事后排查。5.2 核心代码与关键参数我用Python写一个简化版本依赖requests库。核心逻辑如下注意DNSPod API的鉴权和域名ID需要根据实际环境替换import time import requests # 配置区 API_TOKEN 12345,abcdefghijklmnopqrstuvwxyz DOMAIN demo.example.com SUB_DOMAIN demo RECORDS [ {ip: 203.0.113.10, record_id: 111111, fail_count: 0}, {ip: 203.0.113.11, record_id: 222222, fail_count: 0}, ] CHECK_URL_TMPL http://{ip}/healthz FAIL_THRESHOLD 3 SUCCESS_THRESHOLD 3 CHECK_INTERVAL 30 def set_record_status(record_id, action): 调用DNSPod API暂停或启用解析记录 url https://dnsapi.cn/Record.Status data { login_token: API_TOKEN, format: json, domain: DOMAIN, record_id: record_id, status: action, # enable 或 disable } resp requests.post(url, datadata, timeout10) return resp.json() def check_health(ip): 返回True表示健康False表示不健康 try: resp requests.get(CHECK_URL_TMPL.format(ipip), timeout5) return resp.status_code 200 except Exception: return False def main(): last_status {rec[record_id]: unknown for rec in RECORDS} while True: for rec in RECORDS: healthy check_health(rec[ip]) if healthy: rec[fail_count] 0 if last_status[rec[record_id]] disabled: result set_record_status(rec[record_id], enable) print(f{time.strftime(%F %T)} 恢复解析: {rec[ip]}, API返回: {result.get(status, {}).get(message)}) last_status[rec[record_id]] enabled else: rec[fail_count] 1 if rec[fail_count] FAIL_THRESHOLD and last_status[rec[record_id]] enabled: result set_record_status(rec[record_id], disable) print(f{time.strftime(%F %T)} 摘除故障IP: {rec[ip]}, API返回: {result.get(status, {}).get(message)}) last_status[rec[record_id]] disabled time.sleep(CHECK_INTERVAL) if __name__ __main__: main()关键参数需要解释一下FAIL_THRESHOLD 3表示连续3次失败才摘除这是为了过滤偶发的网络抖动。SUCCESS_THRESHOLD 3虽然代码里用了简单的fail_count0实际生产环境建议用独立的success_count来累计连续成功3次才恢复避免瞬时的HTTP 200误判。CHECK_INTERVAL 30意味着从故障发生到摘除最快需要30s * 3 90s再加上TTL生效时间整体切换时间在分钟级。超时设为5秒。超时太短容易被网络抖动误杀太长会拖慢健康检查速度。这个脚本只是一个骨架生产环境还需要考虑多线程并发探测而不是串行、日志轮转、告警通知比如钉钉/企微机器人、API调用失败的重试逻辑等。5.3 切换时间估算有人可能会问这个方案下从故障发生到用户访问被切走到底需要多久我们可以估算一下假设故障发生在T0T0 ~ T05s探测请求发出等待超时或收到错误。T05s ~ T035s第二次探测依然失败。T035s ~ T065s第三次探测失败。此时达到FAIL_THRESHOLD。T065s ~ T075s调用DNSPod API暂停记录API响应成功。之后所有本地DNS缓存没过期的用户可以继续拿到旧IP直到缓存过期。假设TTL60秒最坏情况下还要等60秒。所以从故障到全量切换最坏情况大约是75s 60s ≈ 135s。如果TTL设为120秒那就是约75s 120s ≈ 195s。这个延迟能不能接受要看你的业务容忍度。一般门户类网站可以接受但对核心交易链路这个时间可能就太长了。你可能会想“那把TTL设成1秒不就行了”理论上是可以但会导致每次解析都回源访问速度变慢同时权威DNS压力增大。我的建议是解析记录TTL设置在60秒左右比较平衡既让切换能在可接受时间内完成又不会让DNS解析性能出现明显下降。当然如果域名流量真正大到让权威DNS成为瓶颈那就得上更专业的流量调度系统了。6. 几条容易踩的坑与配置建议6.1 健康探测别只探TCP层最开始我做的健康检查只探测TCP端口是否开放结果吃了大亏。某个应用进程因为连接池耗尽请求大量超时但TCP端口依然能正常建立连接。从探活脚本看一切正常用户却已经打不开页面了。后来我把探测改成HTTP级别的/healthz接口并在业务里实现了对依赖中间件的自检才算比较准确。所以探测协议的选择要根据业务情况来定TCP端口探测适合最基础的存活检查HTTP探测能捕捉到更多应用层异常如果业务有更复杂的依赖关系建议做一个专门的健康检查接口返回JSON状态码让探测程序解析。探测内容越接近真实用户的请求路径判断越准确。6.2 警惕健康检查的“误杀”前面提到探测节点的问题。如果你的后端服务器在安全组或iptables里只放行了特定来源IP健康检查机器的IP不在白名单里那就会产生严重的“误杀”——后端明明好好的却因为探活请求被防火墙拦截被判定为故障然后被摘除。解决方法是把健康检查机器的公网IP明确加入白名单或者让健康检查走内网接口。如果你用云厂商的监控产品还要留意监控节点的IP段经常变化需要及时更新白名单。另一种“误杀”来自探测超时设置不合理。我把超时调成1秒结果某天网络抖动几十台机器被连续摘除线上出现大面积解析失败。后来把超时改为5秒阈值从1次改为连续3次失败再摘除误杀情况就基本消失了。宁可切换慢一点也不要因为误报搞得全局雪崩。6.3 TTL不能无脑设小有些人想“反正要让切换快TTL设1秒”结果解析性能下降、权威DNS流量暴增还可能导致部分运营商DNS主动忽略过短的TTL强制缓存反而让切换更不可控。另一部分人则习惯了长TTL比如一天结果故障时怎么等都不切换用户全部瘫痪。实践经验是普通公网域名的TTL保持在60秒到300秒之间即可。如果你确实需要极快的故障切换最好加一层云负载均衡而不是把所有压力都放在缩短TTL上。6.4 用DNS做高可用的正确姿势最后说一点更宏观的经验。DNS负载均衡这套机制尤其适合做跨地域、跨机房的宏观容灾不适合做机房内部的细粒度故障转移。跨地域容灾时你切换的是“用户流量从上海真身机房切到北京容灾机房”这中间切换的粒度粗、切换频率低故障检测的容错时间可以放宽到几分钟。而同一个机房的几十台Web服务器如果靠DNS做故障切换效果会非常差不仅切换慢而且无法感知连接级的失败。这时候正确做法是上游挂nginx或SLB让它们做健康检查和自动摘除DNS只负责把流量引到负载均衡器上。这样各层各司其职故障转移的能力边界才清晰。另外无论用哪种方案建议都保留“手动总开关”。因为我见过不少自动切换的误判案例最终都是靠运维手动干预才恢复的。自动化能解决90%的常规故障剩下的10%需要人能随时接管。我个人操作下来最深的感觉是DNS负载均衡是一把很粗的调度尺子它能帮你把流量从一个区域挪到另一个区域但别指望它像nginx那样精细地感知每一个后端的呼吸。合理的设计应当是DNS做宏观路径规划负载均衡器做微观流量分发健康检查系统负责连接两层各管一段这样整个架构的容灾能力才会既快又稳。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。