登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  Golang >  Go教程

Go 怎么指定 DNS 服务器解析域名

来源:17golang原创

时间:2026-09-06 05:33:12 351浏览 收藏

Go 默认的域名解析会遵循操作系统和运行环境的解析规则。如果程序需要固定走某台 DNS 服务器,例如内网 DNS 或测试环境的隔离 DNS,不要修改全局系统配置,直接创建 net.Resolver,在它的 Dial 回调里指定服务器地址即可。

最小做法是让 net.Resolver.Dial 返回连向指定 DNS 服务器的连接,再用 LookupHostLookupNetIP 发起查询;如果业务通过 HTTP 访问域名,还要把这个 Resolver 设置到 net.Dialer.Resolver

要点速览
  • PreferGo 影响 Go 解析器与系统解析器的选择,不能单独指定 DNS 地址。
  • Resolver.Dial 才是接入指定 DNS 服务器的位置,回调应使用传入的 networkcontext
  • HTTP 客户端要通过 Transport 使用带 Resolver 的 net.Dialer,否则单独测试的解析器不会自动影响其他请求。

先分清 PreferGo 和指定 DNS 的职责

net.Resolver 解决的是“由谁完成域名解析以及如何建立 DNS 查询连接”。PreferGo: true 表示优先使用纯 Go resolver,但它仍可能读取系统的 DNS 配置;它不是“把服务器改成某个 IP”的开关。要固定服务器,需要实现 Dial

配置或方法解决的问题是否指定服务器
PreferGo偏好 Go resolver 还是系统 resolver
Resolver.DialDNS 查询连接如何建立
GODEBUG=netdns=go调试或切换 resolver 实现
LookupHost发起一次主机名解析使用所属 Resolver
Go net.Resolver、Resolver.Dial、net.Dialer 与指定 DNS 服务器的静态依赖关系图
图1:查看应用解析边界与 DNS 传输边界,理解 Resolver.Dial 如何把 LookupHost 连接到指定服务器。

用 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)
}

回调中的第二个参数是解析器选择的网络类型,常见值包括 udptcp。示例把它继续交给 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 超时和连接复用仍由客户端配置负责。

Go http.Client、http.Transport、net.Dialer.Resolver 与自定义 net.Resolver 的连接边界图
图2:查看 HTTP 客户端边界与连接建立边界,确认自定义 Resolver 是通过 net.Dialer.Resolver 接入 Transport。

常见坑:地址、超时和系统差异

第一,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 服务器”。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>