github.com/sandwich-go/boost@v1.3.29/httputil/dns/dns_imp.go (about)

     1  package dns
     2  
     3  import (
     4  	"context"
     5  	"github.com/sandwich-go/boost/z"
     6  	"net"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  type dns struct {
    13  	Resolver
    14  	opts *Options
    15  }
    16  
    17  // New 创建 dns
    18  func New(opts ...Option) DNS {
    19  	cfg := NewOptions(opts...)
    20  	d := &dns{opts: cfg}
    21  	d.Resolver = d
    22  	return d
    23  }
    24  
    25  // GetDialContext 获取 Dial 函数
    26  func (d *dns) GetDialContext() func(ctx context.Context, network, address string) (net.Conn, error) {
    27  	return func(ctx context.Context, network, addr string) (net.Conn, error) {
    28  		sepIndex := strings.LastIndex(addr, ":")
    29  		ipAddrs, err := d.Resolver.LookupIPAddr(ctx, addr[:sepIndex])
    30  		if err != nil {
    31  			return nil, err
    32  		}
    33  		if len(ipAddrs) == 0 {
    34  			return nil, ErrNotFound
    35  		}
    36  		index := 0
    37  		if d.opts.GetPolicy() == PolicyRandom {
    38  			index = int(z.FastRand()) % len(ipAddrs)
    39  		}
    40  		addr = ipAddrs[index].String() + addr[sepIndex:]
    41  		return d.opts.GetDialer().DialContext(ctx, network, addr)
    42  	}
    43  }
    44  
    45  // LookupIPAddr 通过主机搜索 ip 地址列表
    46  func (d *dns) LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) {
    47  	var start = time.Now()
    48  	var cancel context.CancelFunc
    49  	if _, ok := ctx.Deadline(); !ok {
    50  		ctx, cancel = context.WithTimeout(ctx, d.opts.GetLookupTimeout())
    51  	}
    52  	defer func() {
    53  		if cancel != nil {
    54  			cancel()
    55  		}
    56  	}()
    57  
    58  	ipAddrs, err := d.opts.GetResolver().LookupIPAddr(ctx, host)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  	if len(ipAddrs) == 0 {
    63  		return nil, ErrNotFound
    64  	}
    65  	// 成功则回调
    66  	if d.opts.GetOnLookup() != nil {
    67  		d.opts.GetOnLookup()(ctx, host, time.Since(start), ipAddrs)
    68  	}
    69  	return ipAddrs, nil
    70  }
    71  
    72  type cacheDns struct {
    73  	*dns
    74  	caches sync.Map
    75  }
    76  
    77  // NewCache 创建带缓存的 dns
    78  func NewCache(opts ...Option) CacheDNS {
    79  	dc := &cacheDns{dns: New(opts...).(*dns)}
    80  	dc.dns.Resolver = dc
    81  	return dc
    82  }
    83  
    84  // LookupIPAddr 通过主机搜索 ip 地址列表
    85  func (dc *cacheDns) LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) {
    86  	ipCache, exists := dc.Get(host)
    87  	if exists {
    88  		// 如果创建时间小于0,表示永久有效
    89  		// 如果在有效期内,直接返回
    90  		if ipCache.CreatedAt.IsZero() || ipCache.CreatedAt.Add(dc.opts.GetTTL()).After(time.Now()) {
    91  			return ipCache.IPAddrs, nil
    92  		}
    93  	}
    94  	ipAddrs, err := dc.dns.LookupIPAddr(ctx, host)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	if dc.opts.GetTTL() > 0 {
    99  		dc.Set(host, IpCache{IPAddrs: ipAddrs, CreatedAt: time.Now()})
   100  	} else {
   101  		dc.Set(host, IpCache{IPAddrs: ipAddrs})
   102  	}
   103  	return ipAddrs, nil
   104  }
   105  
   106  func (dc *cacheDns) Set(host string, cache IpCache) {
   107  	dc.caches.Store(host, cache)
   108  }
   109  
   110  func (dc *cacheDns) Remove(host string) {
   111  	dc.caches.Delete(host)
   112  }
   113  
   114  func (dc *cacheDns) Get(host string) (IpCache, bool) {
   115  	c, ok := dc.caches.Load(host)
   116  	if !ok {
   117  		return EmptyIpCache, false
   118  	}
   119  	return c.(IpCache), true
   120  }