Go 怎么指定 DNS 服务器解析域名
来源:17golang原创
时间:2026-09-06 05:33:12 351浏览 收藏
Go 默认的域名解析会遵循操作系统和运行环境的解析规则。如果程序需要固定走某台 DNS 服务器,例如内网 DNS 或测试环境的隔离 DNS,不要修改全局系统配置,直接创建 net.Resolver,在它的 Dial 回调里指定服务器地址即可。
最小做法是让
net.Resolver.Dial返回连向指定 DNS 服务器的连接,再用LookupHost或LookupNetIP发起查询;如果业务通过 HTTP 访问域名,还要把这个 Resolver 设置到net.Dialer.Resolver。
PreferGo影响 Go 解析器与系统解析器的选择,不能单独指定 DNS 地址。Resolver.Dial才是接入指定 DNS 服务器的位置,回调应使用传入的network和context。- HTTP 客户端要通过
Transport使用带 Resolver 的net.Dialer,否则单独测试的解析器不会自动影响其他请求。
先分清 PreferGo 和指定 DNS 的职责
net.Resolver 解决的是“由谁完成域名解析以及如何建立 DNS 查询连接”。PreferGo: true 表示优先使用纯 Go resolver,但它仍可能读取系统的 DNS 配置;它不是“把服务器改成某个 IP”的开关。要固定服务器,需要实现 Dial。
| 配置或方法 | 解决的问题 | 是否指定服务器 |
|---|---|---|
PreferGo | 偏好 Go resolver 还是系统 resolver | 否 |
Resolver.Dial | DNS 查询连接如何建立 | 是 |
GODEBUG=netdns=go | 调试或切换 resolver 实现 | 否 |
LookupHost | 发起一次主机名解析 | 使用所属 Resolver |

用 net.Resolver.Dial 把 DNS 服务器固定下来
下面的 Resolver 只负责把查询交给示例地址 192.0.2.53:53。这个地址是文档示例网段,实际部署时替换成你所在网络允许访问的 DNS 服务器;不要把示例地址直接当成公共 DNS 使用。
package main
import (
"context"
"fmt"
"net"
"time"
)
func main() {
resolver := &net.Resolver{
// 让本次解析优先使用 Go resolver;服务器地址仍由 Dial 决定。
PreferGo: true,
// 同时兼容 DNS 的 UDP/TCP 连接请求,并把取消信号传给拨号器。
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
dialer := &net.Dialer{Timeout: 3 * time.Second}
return dialer.DialContext(ctx, network, "192.0.2.53:53")
},
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// LookupHost 会使用 resolver 的 Dial 回调查询主机地址。
addrs, err := resolver.LookupHost(ctx, "service.internal")
if err != nil {
panic(fmt.Errorf("DNS 解析失败: %w", err))
}
fmt.Println(addrs)
}
回调中的第二个参数是解析器选择的网络类型,常见值包括 udp 和 tcp。示例把它继续交给 DialContext,这样发生截断或需要 TCP 查询时,不会被写死的传输类型卡住。第三个参数是系统默认的 DNS 地址,示例不使用它,所以用下划线忽略。
LookupHost 返回字符串地址列表;如果希望得到带网络类型的地址,可以改用 LookupNetIP(ctx, "ip", host),再根据 netip.Addr 的结果决定 IPv4 或 IPv6 策略。
把自定义解析接到 HTTP 客户端
很多代码只验证了 resolver.LookupHost,却发现 http.Get 仍然走系统 DNS。原因是 HTTP 默认使用自己的 Transport 和 Dialer。正确的连接关系是:把 Resolver 放进 net.Dialer,再把 Dialer 的 DialContext 交给 http.Transport。
resolver := &net.Resolver{
PreferGo: true,
// 只在 Resolver 内固定 DNS;普通 TCP 连接仍由 HTTP 拨号器管理。
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
d := &net.Dialer{Timeout: 3 * time.Second}
return d.DialContext(ctx, network, "192.0.2.53:53")
},
}
dialer := &net.Dialer{
Timeout: 5 * time.Second,
Resolver: resolver, // 只影响这个 Dialer 发起的域名解析。
}
transport := &http.Transport{DialContext: dialer.DialContext}
client := &http.Client{Transport: transport, Timeout: 10 * time.Second}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://service.internal/health", nil)
if err != nil {
return err
}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
这个片段需要补上 net/http 导入,并把 ctx 放在同一请求函数中。生产代码还应检查响应状态并限制响应体读取大小。重要的是,Resolver 只负责“域名到地址”的阶段,TLS 校验、HTTP 超时和连接复用仍由客户端配置负责。

常见坑:地址、超时和系统差异
第一,DNS 服务器地址必须带端口,通常是 :53;传给 DialContext 的地址不能只写裸 IP。第二,不要在回调里重新创建没有超时的拨号器,否则上层 context 取消之外,还缺少独立的 DNS 建连上限。第三,固定 DNS 并不等于固定最终 IP,DNS 仍可能返回多条地址,HTTP 客户端还会受到 IPv4/IPv6、代理和连接池策略影响。
在 Unix 环境中,Go 的名称解析可能在纯 Go resolver 与 cgo resolver 之间选择;GODEBUG=netdns=1 可以打印选择信息,适合排查“代码看似指定了 Resolver,但请求仍不符合预期”的情况。不要把这个调试开关当成业务配置,也不要用它替代 Resolver.Dial。
相关问题
只设置 PreferGo 能不能指定 DNS?
不能。PreferGo 只影响 resolver 实现偏好;要指定服务器,需要在 Resolver.Dial 中建立到目标 DNS 的连接。
Resolver.Dial 中应该固定 udp 还是 tcp?
优先使用回调传入的 network,让解析器决定采用哪种 DNS 传输。除非你的网络环境有明确限制,否则不建议无条件改写成单一协议。
为什么 LookupHost 生效但 http.Get 不生效?
两者可能使用了不同的 Resolver。把自定义 Resolver 放入 net.Dialer.Resolver,并把 Dialer.DialContext 交给 HTTP Transport,才能让该客户端复用这套解析配置。
把 DNS 配置收拢在客户端边界内
需要指定 DNS 时,优先把配置封装到一个可复用的 net.Resolver 和 HTTP 客户端中,配合请求级 context 控制超时与取消。这样既不会改动宿主机的全局解析设置,也能在测试、内网服务和多环境部署中明确看出“哪一个客户端使用哪一台 DNS 服务器”。
-
215 收藏
-
399 收藏
-
458 收藏
-
151 收藏
-
169 收藏
-
397 收藏
-
473 收藏
-
288 收藏
-
233 收藏
-
485 收藏
-
115 收藏
-
192 收藏
-
285 收藏
-
322 收藏
-
Golang · Go教程 | 2小时前 | HTTP · sse · Go教程 · 实时推送 · Go EventSource SSE Server-Sent Events http.Flusher215 收藏
-
175 收藏
-
283 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习