下篇文章我们从源码剖析了gonet库dns在类unix平台的解析逻辑原理,这篇文章我们主要从实战角度去解决go程序由于没有dns缓存造成的io阻塞
验证cgo和go的解析性能测试goResolver和CgoResolver
func ResolverBaidu() ([]string,error) {
resolver := &net.Resolver{PreferGo: false}
ctx := context.Background()
return resolver.LookupHost(ctx,"www.baidu.com")
}
func BenchmarkResolverBaidu(b *testing.B) {
for i := 0; i < b.N; i++ {
_,err := ResolverBaidu()
if err == nil {
continue
}
}
}
[root@test-go-build-87 netResolverTest]# export GODEBUG=netdns=go;go test --bench=.
goos: linux
goarch: amd64
pkg: netResolverTest
BenchmarkResolverBaidu-8 453 2725275 ns/op
PASS
ok netResolverTest 1.514s
[root@test-go-build-87 netResolverTest]# export GODEBUG=netdns=cgo;go test --bench=.
goos: linux
goarch: amd64
pkg: netResolverTest
BenchmarkResolverBaidu-8 458 3810726 ns/op
PASS
ok netResolverTest 2.030s
可以看下来在没有缓存的情况下c和go的解析基本是差不多的
为cgo提供缓存
# 安装和启动
yum/apt install nscd
service nscd start
# 再来测试下
[root@test-go-build-87 netResolverTest]# export GODEBUG=netdns=cgo;go test --bench=.
goos: linux
goarch: amd64
pkg: netResolverTest
BenchmarkResolverBaidu-8 57067 20947 ns/op
PASS
ok netResolverTest 1.429s
你没有看错,这就是真实的结果,带缓存的cgo解析性能是go解析性能的100倍还多,那你会不会认为,就直接使用cgo解析+nscd就好了,并且请不要忘掉cgonotgo深度linux系统,虽然有了上述测试结果linux系统平台,我们就可以想了,在没有解析的情况下,cgo和go的解析基准测试性能都差不多,那假如给go加上缓存,是不是解析性能也会有质的飞越?
为Go实现解析缓存
package resolverCache
import (
"context"
"math/rand"
"net"
"reflect"
"sync"
"time"
)
var store = make(map[string][]string)
type resolverCache struct {
Context context.Context
Resolver *net.Resolver
Time time.Duration
Lock sync.RWMutex
}
func New() *resolverCache {
ctx := context.Background()
r := &resolverCache{
Context: ctx,
}
if r.Time == 0 {
r.Time = time.Second * 30
}
go func() {
for {
for k,v :=range store {
ips,err := r.lookup(k)
if err != nil {
continue
}
if reflect.DeepEqual(v,ips) {
continue
}
r.store(k,ips)
}
time.Sleep(r.Time)
}
}()
return r
}
func (r *resolverCache)LookupHosts(domain string) ([]string, error) {
return r.lookup(domain)
}
func (r *resolverCache)LookupOneHost(domain string) string {
ips,err := r.lookup(domain)
if err != nil {
return ""
}
rands := rand.New(rand.NewSource(time.Now().UnixNano()))
index :=rands.Intn(len(ips))
return ips[index]
}
func (r *resolverCache) lookup(domain string) ([]string, error) {
var (
value interface{}
ok bool
)
r.Lock.RLock()
value, ok = store[domain];
r.Lock.RUnlock()
if !ok {
return r.lookupHost(domain)
}
return value.([]string), nil
}
func (r *resolverCache) lookupHost(domain string) ([]string, error) {
ips,err :=r.Resolver.LookupHost(r.Context, domain)
if err == nil {
r.store(domain,ips)
}
return ips,err
}
func (r *resolverCache) store(domain string,ips []string) {
r.Lock.Lock()
store[domain]= ips
r.Lock.Unlock()
}
var resolve = resolverCache.New()
func ResolverBaiduUseCache(host string) ([]string,error) {
ips,err := resolve.LookupHosts(host)
return ips,err
}
func BenchmarkNameResolverBaiduUseCache(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := ResolverBaiduUseCache(host)
if err == nil {
continue
}
}
}
[root@test-go-build-87 netResolverTest]# export GODEBUG=netdns=cgo;go test --bench=. -benchtime=3s -count=3
goos: linux
goarch: amd64
pkg: netResolverTest
BenchmarkResolverBaidu-8 169261 21918 ns/op
BenchmarkResolverBaidu-8 162778 21683 ns/op
BenchmarkResolverBaidu-8 175989 21429 ns/op
BenchmarkNameResolverBaiduUseCache-8 35489584 110 ns/op
BenchmarkNameResolverBaiduUseCache-8 31780479 116 ns/op
BenchmarkNameResolverBaiduUseCache-8 29593422 105 ns/op
PASS
ok netResolverTest 22.758s
可以看出linux系统平台,nscd实现的缓存使cgo解析性能其实领先没有缓存的go解析linux 分区,而且我们实现的这个缓存把nscd缓存远远甩在身旁。
最后
通过前面源码阅读剖析和代码实战,你应当早已很清楚go语言在域名解析这块的机制,里面dnscache代码已然开源地址/chulinx/dnscache