From b6a0e4d983816d9b1cff12c0a67fd90f35c4ec17 Mon Sep 17 00:00:00 2001 From: Vasiliy Tolstov Date: Mon, 9 Dec 2024 00:41:08 +0300 Subject: [PATCH] add metrics for dns Signed-off-by: Vasiliy Tolstov --- client/noop.go | 4 ++-- semconv/cache.go | 14 ++++++++++++++ semconv/store.go | 2 +- service.go | 5 ++++- util/dns/cache.go | 35 ++++++++++++++++++++++++++++++++++- util/dns/cache_test.go | 8 +++++++- util/dns/conn.go | 12 +++++------- 7 files changed, 67 insertions(+), 13 deletions(-) create mode 100644 semconv/cache.go diff --git a/client/noop.go b/client/noop.go index ad0fc2a8..dc24e1ec 100644 --- a/client/noop.go +++ b/client/noop.go @@ -222,7 +222,7 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt ts := time.Now() n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc() var sp tracer.Span - ctx, sp = n.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client", + ctx, sp = n.opts.Tracer.Start(ctx, "rpc-client", tracer.WithSpanKind(tracer.SpanKindClient), tracer.WithSpanLabels("endpoint", req.Endpoint()), ) @@ -385,7 +385,7 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption ts := time.Now() n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc() var sp tracer.Span - ctx, sp = n.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client", + ctx, sp = n.opts.Tracer.Start(ctx, "rpc-client", tracer.WithSpanKind(tracer.SpanKindClient), tracer.WithSpanLabels("endpoint", req.Endpoint()), ) diff --git a/semconv/cache.go b/semconv/cache.go new file mode 100644 index 00000000..d23d0cb5 --- /dev/null +++ b/semconv/cache.go @@ -0,0 +1,14 @@ +package semconv + +var ( + // CacheRequestDurationSeconds specifies meter metric name + CacheRequestDurationSeconds = "micro_cache_request_duration_seconds" + // CacheRequestLatencyMicroseconds specifies meter metric name + CacheRequestLatencyMicroseconds = "micro_cache_request_latency_microseconds" + // CacheRequestTotal specifies meter metric name + CacheRequestTotal = "micro_cache_request_total" + // CacheRequestInflight specifies meter metric name + CacheRequestInflight = "micro_cache_request_inflight" + // CacheItemsTotal specifies total cache items + CacheItemsTotal = "micro_cache_items_total" +) diff --git a/semconv/store.go b/semconv/store.go index a9044f48..f1f2e684 100644 --- a/semconv/store.go +++ b/semconv/store.go @@ -3,7 +3,7 @@ package semconv var ( // StoreRequestDurationSeconds specifies meter metric name StoreRequestDurationSeconds = "micro_store_request_duration_seconds" - // ClientRequestLatencyMicroseconds specifies meter metric name + // StoreRequestLatencyMicroseconds specifies meter metric name StoreRequestLatencyMicroseconds = "micro_store_request_latency_microseconds" // StoreRequestTotal specifies meter metric name StoreRequestTotal = "micro_store_request_total" diff --git a/service.go b/service.go index 469e823c..b5b053f8 100644 --- a/service.go +++ b/service.go @@ -34,7 +34,10 @@ func init() { ), ) - net.DefaultResolver = utildns.NewNetResolver(utildns.Timeout(1 * time.Second)) + net.DefaultResolver = utildns.NewNetResolver( + utildns.Timeout(1*time.Second), + utildns.MinCacheTTL(5*time.Second), + ) } // Service is an interface that wraps the lower level components. diff --git a/util/dns/cache.go b/util/dns/cache.go index 051cc041..3322b698 100644 --- a/util/dns/cache.go +++ b/util/dns/cache.go @@ -6,6 +6,9 @@ import ( "net" "sync" "time" + + "go.unistack.org/micro/v3/meter" + "go.unistack.org/micro/v3/semconv" ) // DialFunc is a [net.Resolver.Dial] function. @@ -19,6 +22,11 @@ func NewNetResolver(opts ...Option) *net.Resolver { o(&options) } + if options.Meter == nil { + options.Meter = meter.DefaultMeter + opts = append(opts, Meter(options.Meter)) + } + return &net.Resolver{ PreferGo: true, StrictErrors: options.Resolver.StrictErrors, @@ -56,6 +64,7 @@ type Options struct { PreferIPV4 bool PreferIPV6 bool Timeout time.Duration + Meter meter.Meter } // MaxCacheEntries sets the maximum number of entries to cache. @@ -87,6 +96,13 @@ func NegativeCache(b bool) Option { } } +// Meter sets meter.Meter +func Meter(m meter.Meter) Option { + return func(o *Options) { + o.Meter = m + } +} + // Timeout sets upstream *net.Resolver timeout func Timeout(td time.Duration) Option { return func(o *Options) { @@ -156,7 +172,6 @@ func (c *cache) put(req string, res string) { } c.Lock() - defer c.Unlock() if c.entries == nil { c.entries = make(map[string]cacheEntry) } @@ -165,6 +180,8 @@ func (c *cache) put(req string, res string) { var tested, evicted int for k, e := range c.entries { if time.Until(e.deadline) <= 0 { + c.opts.Meter.Counter(semconv.CacheItemsTotal, "type", "dns").Dec() + c.opts.Meter.Counter(semconv.CacheRequestTotal, "type", "dns", "method", "evict").Inc() // delete expired entry delete(c.entries, k) evicted++ @@ -175,6 +192,8 @@ func (c *cache) put(req string, res string) { continue } if evicted == 0 && c.opts.MaxCacheEntries > 0 && len(c.entries) >= c.opts.MaxCacheEntries { + c.opts.Meter.Counter(semconv.CacheItemsTotal, "type", "dns").Dec() + c.opts.Meter.Counter(semconv.CacheRequestTotal, "type", "dns", "method", "evict").Inc() // delete at least one entry delete(c.entries, k) } @@ -186,6 +205,9 @@ func (c *cache) put(req string, res string) { deadline: time.Now().Add(ttl), value: res[2:], } + + c.opts.Meter.Counter(semconv.CacheItemsTotal, "type", "dns").Inc() + c.Unlock() } func (c *cache) get(req string) (res string) { @@ -210,6 +232,7 @@ func (c *cache) get(req string) (res string) { // prepend correct ID return req[:2] + entry.value } + return "" } @@ -310,10 +333,19 @@ func getUint32(s string) int { func cachingRoundTrip(cache *cache, network, address string) roundTripper { return func(ctx context.Context, req string) (res string, err error) { + cache.opts.Meter.Counter(semconv.CacheRequestInflight, "type", "dns").Inc() + defer cache.opts.Meter.Counter(semconv.CacheRequestInflight, "type", "dns").Dec() // check cache if res := cache.get(req); res != "" { + cache.opts.Meter.Counter(semconv.CacheRequestTotal, "type", "dns", "method", "get", "status", "hit").Inc() return res, nil } + cache.opts.Meter.Counter(semconv.CacheRequestTotal, "type", "dns", "method", "get", "status", "miss").Inc() + ts := time.Now() + defer func() { + cache.opts.Meter.Summary(semconv.CacheRequestLatencyMicroseconds, "type", "dns", "method", "get").UpdateDuration(ts) + cache.opts.Meter.Histogram(semconv.CacheRequestDurationSeconds, "type", "dns", "method", "get").UpdateDuration(ts) + }() switch { case cache.opts.PreferIPV4 && cache.opts.PreferIPV6: @@ -340,6 +372,7 @@ func cachingRoundTrip(cache *cache, network, address string) roundTripper { var d net.Dialer conn, err = d.DialContext(ctx, network, address) } + if err != nil { return "", err } diff --git a/util/dns/cache_test.go b/util/dns/cache_test.go index 6e2fb1d1..1c939145 100644 --- a/util/dns/cache_test.go +++ b/util/dns/cache_test.go @@ -12,5 +12,11 @@ func TestCache(t *testing.T) { if err != nil { t.Fatal(err) } - t.Logf("addrs %v", addrs) + + addrs, err = net.LookupHost("unistack.org") + if err != nil { + t.Fatal(err) + } + + _ = addrs } diff --git a/util/dns/conn.go b/util/dns/conn.go index 8d71ad46..8cbb95e7 100644 --- a/util/dns/conn.go +++ b/util/dns/conn.go @@ -11,15 +11,13 @@ import ( ) type dnsConn struct { - sync.Mutex - - ibuf bytes.Buffer - obuf bytes.Buffer - + deadline time.Time ctx context.Context cancel context.CancelFunc - deadline time.Time roundTrip roundTripper + ibuf bytes.Buffer + obuf bytes.Buffer + sync.Mutex } type roundTripper func(ctx context.Context, req string) (res string, err error) @@ -78,8 +76,8 @@ func (c *dnsConn) SetDeadline(t time.Time) error { func (c *dnsConn) SetReadDeadline(t time.Time) error { c.Lock() - defer c.Unlock() c.deadline = t + c.Unlock() return nil }