Chore: reduce dhcp dns client cost

This commit is contained in:
Dreamacro 2022-08-24 21:36:19 +08:00
parent bf079742cb
commit a3281712e2
3 changed files with 44 additions and 35 deletions

View file

@ -29,7 +29,7 @@ type dhcpClient struct {
ifaceAddr *net.IPNet ifaceAddr *net.IPNet
done chan struct{} done chan struct{}
resolver *Resolver clients []dnsClient
err error err error
} }
@ -41,15 +41,15 @@ func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
} }
func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
res, err := d.resolve(ctx) clients, err := d.resolve(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return res.ExchangeContext(ctx, m) return batchExchange(ctx, clients, m)
} }
func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {
d.lock.Lock() d.lock.Lock()
invalidated, err := d.invalidate() invalidated, err := d.invalidate()
@ -64,8 +64,9 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout) ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout)
defer cancel() defer cancel()
var res *Resolver var res []dnsClient
dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName) dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName)
// dns never empty if err is nil
if err == nil { if err == nil {
nameserver := make([]NameServer, 0, len(dns)) nameserver := make([]NameServer, 0, len(dns))
for _, item := range dns { for _, item := range dns {
@ -75,9 +76,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
}) })
} }
res = NewResolver(Config{ res = transform(nameserver, nil)
Main: nameserver,
})
} }
d.lock.Lock() d.lock.Lock()
@ -86,7 +85,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
close(done) close(done)
d.done = nil d.done = nil
d.resolver = res d.clients = res
d.err = err d.err = err
}() }()
} }
@ -96,7 +95,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) {
for { for {
d.lock.Lock() d.lock.Lock()
res, err, done := d.resolver, d.err, d.done res, err, done := d.clients, d.err, d.done
d.lock.Unlock() d.lock.Unlock()

View file

@ -10,7 +10,6 @@ import (
"time" "time"
"github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
@ -187,31 +186,10 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
} }
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout) ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDNSTimeout)
for _, client := range clients { defer cancel()
r := client
fast.Go(func() (any, error) {
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
return m, nil
})
}
elm := fast.Wait() return batchExchange(ctx, clients, m)
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm.(*D.Msg)
return
} }
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {

View file

@ -1,11 +1,15 @@
package dns package dns
import ( import (
"context"
"crypto/tls" "crypto/tls"
"errors"
"fmt"
"net" "net"
"time" "time"
"github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
D "github.com/miekg/dns" D "github.com/miekg/dns"
@ -102,3 +106,31 @@ func msgToIP(msg *D.Msg) []net.IP {
return ips return ips
} }
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithContext(ctx)
for _, client := range clients {
r := client
fast.Go(func() (any, error) {
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
return m, nil
})
}
elm := fast.Wait()
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm.(*D.Msg)
return
}