← 中文资源

简体中文版本

带超时的 DNS 查询

通过默认解析器在上下文截止时间内查询主机名,排序返回全部地址,并清晰处理解析失败。

阅读英文原文

功能说明

服务启动时的依赖检查、出站白名单校验和运维脚本经常需要解析域名。直接调用 net.LookupHost 无法设置超时,遇到慢解析或网络故障时可能阻塞很久。本例使用 net.DefaultResolver.LookupHost 并传入带截止时间的上下文,解析器会在超时或取消时返回错误,调用方可以快速失败并决定是否重试。

一个域名可能解析出多个 IPv4 与 IPv6 地址,顺序由解析器和 DNS 响应决定。函数在返回前排序,让日志和测试结果稳定,也方便调用方按确定顺序尝试连接。

关键代码

Lookup 接收上下文和主机名,调用默认解析器查询全部地址;出错时返回 nil 与原始错误,避免返回半截结果。成功时用 sort.Strings 原地排序。main 创建两秒超时的上下文,并始终调用 cancel 释放计时器。解析 localhost 后逐行打印地址;失败分支打印错误并提前返回,不继续使用空结果。

使用注意

上下文超时只约束解析调用,不保证底层系统解析器立即停止,某些平台上仍可能短暂占用资源。生产环境应同时配置 DNS 缓存和重试,并区分临时失败与域名不存在。排序后的地址不代表优选顺序,Happy Eyeballs 等连接策略会自行选择协议。不要信任解析结果做安全判断,DNS 可以被伪造,敏感场景应使用 DNSSEC 或固定证书校验。解析结果可能变化,不要长期缓存到配置文件中。

验证方式

运行脚本,应输出本机回环地址,通常包含 127.0.0.1 与 ::1,顺序稳定。把主机名改成不存在的域名,两秒内应返回 lookup failed 信息而不是长时间挂起。把超时改为纳秒级,可观察上下文截止错误。

代码示例

package main

import (
	"context"
	"fmt"
	"net"
	"sort"
	"time"
)

func Lookup(ctx context.Context, host string) ([]string, error) {
	addresses, err := net.DefaultResolver.LookupHost(ctx, host)
	if err != nil {
		return nil, err
	}
	sort.Strings(addresses)
	return addresses, nil
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	addresses, err := Lookup(ctx, "localhost")
	if err != nil {
		fmt.Println("lookup failed:", err)
		return
	}
	for _, address := range addresses {
		fmt.Println(address)
	}
}