Merge remote-tracking branch 'origin/v3' into v3
# Conflicts: # client/noop.go # errors/errors_test.go # logger/unwrap/unwrap_test.go # register/memory/memory_test.go # server/noop_test.go # service.go # util/dns/cache.go # util/dns/conn.go # util/structfs/metadata_ec2.go # util/time/duration.go
This commit is contained in:
@@ -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,18 @@ 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 != "" {
|
||||
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 +371,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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -67,15 +67,20 @@ func (c *dnsConn) RemoteAddr() net.Addr {
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetDeadline(t time.Time) error {
|
||||
_ = c.SetReadDeadline(t)
|
||||
_ = c.SetWriteDeadline(t)
|
||||
var err error
|
||||
if err = c.SetReadDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.SetWriteDeadline(t); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *dnsConn) SetReadDeadline(t time.Time) error {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.deadline = t
|
||||
c.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ type Ticker struct {
|
||||
C chan time.Time
|
||||
min int64
|
||||
max int64
|
||||
exp int64
|
||||
exit bool
|
||||
rng rand.Rand
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error {
|
||||
}
|
||||
|
||||
if mapper, ok := dst.(map[string]interface{}); ok {
|
||||
dst = mergeMap(mapper, mp, 0)
|
||||
mergeMap(mapper, mp, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,38 @@
|
||||
package reflect
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMergeMapStringInterface(t *testing.T) {
|
||||
var dst interface{} //nolint:gosimple
|
||||
dst = map[string]interface{}{
|
||||
"xx": 11,
|
||||
}
|
||||
|
||||
src := map[string]interface{}{
|
||||
"zz": "aa",
|
||||
}
|
||||
|
||||
if err := Merge(dst, src); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
mp, ok := dst.(map[string]interface{})
|
||||
if !ok || mp == nil {
|
||||
t.Fatalf("xxx %#+v\n", dst)
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%v", mp["xx"]) != "11" {
|
||||
t.Fatalf("xxx zzzz %#+v", mp)
|
||||
}
|
||||
|
||||
if fmt.Sprintf("%v", mp["zz"]) != "aa" {
|
||||
t.Fatalf("xxx zzzz %#+v", mp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMergeMap(t *testing.T) {
|
||||
src := map[string]interface{}{
|
||||
"skey1": "sval1",
|
||||
|
||||
@@ -221,6 +221,7 @@ func getValue(name string, iface interface{}, tag string) ([]byte, error) {
|
||||
return nil, fmt.Errorf("failed to find %s in interface %T", name, iface)
|
||||
}
|
||||
|
||||
/*
|
||||
func hasValidType(obj interface{}, types []reflect.Kind) bool {
|
||||
for _, t := range types {
|
||||
if reflect.TypeOf(obj).Kind() == t {
|
||||
@@ -230,6 +231,7 @@ func hasValidType(obj interface{}, types []reflect.Kind) bool {
|
||||
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
func reflectValue(obj interface{}) reflect.Value {
|
||||
var val reflect.Value
|
||||
|
||||
@@ -61,7 +61,7 @@ var doOrig = []byte(`{
|
||||
}
|
||||
`)
|
||||
|
||||
func server(t *testing.T) {
|
||||
func server(t *testing.T, ch chan error) {
|
||||
stfs := DigitalOceanMetadata{}
|
||||
err := json.Unmarshal(doOrig, &stfs.Metadata.V1)
|
||||
if err != nil {
|
||||
@@ -71,7 +71,7 @@ func server(t *testing.T) {
|
||||
http.Handle("/metadata/v1/", FileServer(&stfs, "json", time.Now()))
|
||||
http.Handle("/metadata/v1.json", &stfs)
|
||||
go func() {
|
||||
t.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
|
||||
ch <- http.ListenAndServe("127.0.0.1:8080", nil)
|
||||
}()
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
@@ -86,7 +86,8 @@ func get(path string) ([]byte, error) {
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
server(t)
|
||||
ch := make(chan error)
|
||||
server(t, ch)
|
||||
|
||||
tests := []struct {
|
||||
in string
|
||||
@@ -100,34 +101,44 @@ func TestAll(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
buf, err := get(tt.in)
|
||||
select {
|
||||
case err := <-ch:
|
||||
t.Fatal(err)
|
||||
default:
|
||||
buf, err := get(tt.in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(buf) != tt.out {
|
||||
t.Errorf("req %s output %s not match requested %s", tt.in, string(buf), tt.out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-ch:
|
||||
t.Fatal(err)
|
||||
default:
|
||||
doTest, err := get("http://127.0.0.1:8080/metadata/v1.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(buf) != tt.out {
|
||||
t.Errorf("req %s output %s not match requested %s", tt.in, string(buf), tt.out)
|
||||
|
||||
oSt := DigitalOceanMetadata{}
|
||||
err = json.Unmarshal(doOrig, &oSt.Metadata.V1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nSt := DigitalOceanMetadata{}
|
||||
|
||||
err = json.Unmarshal(doTest, &nSt.Metadata.V1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(oSt, nSt) {
|
||||
t.Fatalf("%v not match %v", oSt, nSt)
|
||||
}
|
||||
}
|
||||
|
||||
doTest, err := get("http://127.0.0.1:8080/metadata/v1.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
oSt := DigitalOceanMetadata{}
|
||||
err = json.Unmarshal(doOrig, &oSt.Metadata.V1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
nSt := DigitalOceanMetadata{}
|
||||
|
||||
err = json.Unmarshal(doTest, &nSt.Metadata.V1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(oSt, nSt) {
|
||||
t.Fatalf("%v not match %v", oSt, nSt)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user