NameserverNameserverGolangNameserver

DNS解析过程

Golangnet.ResolverLookupHost(ctx context.Context, host string) (addrs []string, err error)
hostsresolv.confnameservernameservernameservernameserver
/etc/resolv.confnameserverkubernetessidecarservicednsPolicyClusterFirst

自定义Nameserver

GolangNameserverResolverhttpClientDialContext()
Resolver
// 默认dialer
dialer := &net.Dialer{
		Timeout: 1 * time.Second,
}

// 定义resolver
resolver := &net.Resolver{
	Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
		return dialer.DialContext(ctx, "tcp", nameserver) // 通过tcp请求nameserver解析域名
	},
}
Dialer
type Dialer struct {
	dialer     *net.Dialer
	resolver   *net.Resolver
	nameserver string
}

// NewDialer create a Dialer with user's nameserver.
func NewDialer(dialer *net.Dialer, nameserver string) (*Dialer, error) {
	conn, err := dialer.Dial("tcp", nameserver)
	if err != nil {
		return nil, err
	}
	defer conn.Close()

	return &Dialer{
		dialer: dialer,
		resolver: &net.Resolver{
			Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
				return dialer.DialContext(ctx, "tcp", nameserver)
			},
		},
		nameserver: nameserver, // 用户设置的nameserver
	}, nil
}

// DialContext connects to the address on the named network using
// the provided context.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
	host, port, err := net.SplitHostPort(address)
	if err != nil {
		return nil, err
	}

	ips, err := d.resolver.LookupHost(ctx, host) // 通过自定义nameserver查询域名
	for _, ip := range ips {
    // 创建链接
		conn, err := d.dialer.DialContext(ctx, network, ip+":"+port)
		if err == nil {
			return conn, nil
		}
	}

	return d.dialer.DialContext(ctx, network, address)
}
httpClientDialContext()
ndialer, _ := NewDialer(dialer, nameserver)

client := &http.Client{
  Transport: &http.Transport{
    DialContext:         ndialer.DialContext,
    TLSHandshakeTimeout: 10 * time.Second,
  },
  Timeout: timeout,
}

总结

NameserverDailer