diff --git a/CHANGE.md b/CHANGE.md index 4a75ad3519fce9e8fe52fabc516d4a66c6f7b135..90cb5a1443e8062125cbb9f2b3cc0ac1cf759d06 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,6 +1,34 @@ # Release Notes --- +## 1.5.1 + +### New Features +- [Add dynamic tag router](https://github.com/apache/dubbo-go/pull/703) +- [Add TLS support](https://github.com/apache/dubbo-go/pull/685) +- [Add Nearest first for multiple registry](https://github.com/apache/dubbo-go/pull/659) +- [Add application and service level router](https://github.com/apache/dubbo-go/pull/662) +- [Add dynamic tag router](https://github.com/apache/dubbo-go/pull/665) + +### Enhancement +- [Avoid init the log twice](https://github.com/apache/dubbo-go/pull/719) +- [Correct words and format codes](https://github.com/apache/dubbo-go/pull/704) +- [Change log stack level from warn to error](https://github.com/apache/dubbo-go/pull/702) +- [Optimize remotes configuration](https://github.com/apache/dubbo-go/pull/687) + +### Bugfixes +- [Fix register service instance after provider config load](https://github.com/apache/dubbo-go/pull/694) +- [Fix call subscribe function asynchronously](https://github.com/apache/dubbo-go/pull/721) +- [Fix tag router rule copy](https://github.com/apache/dubbo-go/pull/721) +- [Fix nacos unit test failed](https://github.com/apache/dubbo-go/pull/705) +- [Fix can not inovke nacos destroy when graceful shutdown](https://github.com/apache/dubbo-go/pull/689) +- [Fix zk lost event](https://github.com/apache/dubbo-go/pull/692) +- [Fix k8s ut bug](https://github.com/apache/dubbo-go/pull/693) + +Milestone: [https://github.com/apache/dubbo-go/milestone/2?closed=1](https://github.com/apache/dubbo-go/milestone/2?closed=1) + +Project: [https://github.com/apache/dubbo-go/projects/8](https://github.com/apache/dubbo-go/projects/8) + ## 1.5.0 ### New Features diff --git a/README.md b/README.md index 7df7e7973a7e3e808f4b8feb4f26b98c0f21e9ef..9e1edd3af1cd8957b1daa9b9fe2cadf121bc2d6d 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Apache License, Version 2.0 ## Release note ## +[v1.5.1 - Aug 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.1) + [v1.5.0 - July 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0) [v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0) diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go index 87f51356495dbd0a956c42bf4f34022b4d21ad4d..6d75dff5da09cc18828c9105d257c8f80b8de885 100644 --- a/cluster/directory/static_directory.go +++ b/cluster/directory/static_directory.go @@ -39,10 +39,13 @@ func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory { if len(invokers) > 0 { url = invokers[0].GetUrl() } - return &staticDirectory{ + dir := &staticDirectory{ BaseDirectory: NewBaseDirectory(&url), invokers: invokers, } + + dir.routerChain.SetInvokers(invokers) + return dir } //for-loop invokers ,if all invokers is available ,then it means directory is available @@ -69,7 +72,7 @@ func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invo return invokers } dirUrl := dir.GetUrl() - return routerChain.Route(invokers, &dirUrl, invocation) + return routerChain.Route(&dirUrl, invocation) } // Destroy Destroy @@ -92,6 +95,7 @@ func (dir *staticDirectory) BuildRouterChain(invokers []protocol.Invoker) error if e != nil { return e } + routerChain.SetInvokers(dir.invokers) dir.SetRouterChain(routerChain) return nil } diff --git a/cluster/router/chain.go b/cluster/router/chain.go new file mode 100644 index 0000000000000000000000000000000000000000..3614d0a5a3d6cfb462ef63149ae99da2c4541b8d --- /dev/null +++ b/cluster/router/chain.go @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package router + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +// Chain +type Chain interface { + Route(*common.URL, protocol.Invocation) []protocol.Invoker + // Refresh invokers + SetInvokers([]protocol.Invoker) + // AddRouters Add routers + AddRouters([]PriorityRouter) +} diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go index 8746c1daf7f878a066ea005f910520e07c28318c..952aedf92d70d92b3b029a9809826295e1cc7dc5 100644 --- a/cluster/router/chain/chain.go +++ b/cluster/router/chain/chain.go @@ -18,9 +18,10 @@ package chain import ( - "math" "sort" "sync" + "sync/atomic" + "time" ) import ( @@ -30,11 +31,18 @@ import ( import ( "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" ) +const ( + timeInterval = 5 * time.Second + timeThreshold = 2 * time.Second + countThreshold = 5 +) + // RouterChain Router chain type RouterChain struct { // Full list of addresses from registry, classified by method name. @@ -48,30 +56,38 @@ type RouterChain struct { mutex sync.RWMutex url common.URL + + // The times of address notification since last update for address cache + count int64 + // The timestamp of last update for address cache + last time.Time + // Channel for notify to update the address cache + notify chan struct{} + // Address cache + cache atomic.Value } // Route Loop routers in RouterChain and call Route method to determine the target invokers list. -func (c *RouterChain) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - finalInvokers := invokers - l := len(c.routers) - rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2))) - c.mutex.RLock() - copy(rs, c.routers) - c.mutex.RUnlock() +func (c *RouterChain) Route(url *common.URL, invocation protocol.Invocation) []protocol.Invoker { + cache := c.loadCache() + if cache == nil { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.invokers + } - for _, r := range rs { - finalInvokers = r.Route(finalInvokers, url, invocation) + bitmap := cache.bitmap + for _, r := range c.copyRouters() { + bitmap = r.Route(bitmap, cache, url, invocation) } - return finalInvokers -} -// SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change. -func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) { - for _, r := range c.routers { - if notifyRouter, ok := r.(router.NotifyRouter); ok { - notifyRouter.Notify(invokers) - } + indexes := bitmap.ToArray() + finalInvokers := make([]protocol.Invoker, len(indexes)) + for i, index := range indexes { + finalInvokers[i] = cache.invokers[index] } + + return finalInvokers } // AddRouters Add routers to router chain @@ -88,6 +104,116 @@ func (c *RouterChain) AddRouters(routers []router.PriorityRouter) { c.routers = newRouters } +// SetInvokers receives updated invokers from registry center. If the times of notification exceeds countThreshold and +// time interval exceeds timeThreshold since last cache update, then notify to update the cache. +func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) { + c.mutex.Lock() + c.invokers = invokers + c.mutex.Unlock() + + c.count++ + now := time.Now() + if c.count >= countThreshold && now.Sub(c.last) >= timeThreshold { + c.last = now + c.count = 0 + go func() { + c.notify <- struct{}{} + }() + } +} + +// loop listens on events to update the address cache when it's necessary, either when it receives notification +// from address update, or when timeInterval exceeds. +func (c *RouterChain) loop() { + for { + ticker := time.NewTicker(timeInterval) + select { + case <-ticker.C: + c.buildCache() + case <-c.notify: + c.buildCache() + } + } +} + +// copyRouters make a snapshot copy from RouterChain's router list. +func (c *RouterChain) copyRouters() []router.PriorityRouter { + c.mutex.RLock() + defer c.mutex.RUnlock() + ret := make([]router.PriorityRouter, 0, len(c.routers)) + ret = append(ret, c.routers...) + return ret +} + +// copyInvokers copies a snapshot of the received invokers. +func (c *RouterChain) copyInvokers() []protocol.Invoker { + c.mutex.RLock() + defer c.mutex.RUnlock() + if c.invokers == nil || len(c.invokers) == 0 { + return nil + } + ret := make([]protocol.Invoker, 0, len(c.invokers)) + ret = append(ret, c.invokers...) + return ret +} + +// loadCache loads cache from sync.Value to guarantee the visibility +func (c *RouterChain) loadCache() *InvokerCache { + v := c.cache.Load() + if v == nil { + return nil + } + + return v.(*InvokerCache) +} + +// copyInvokerIfNecessary compares chain's invokers copy and cache's invokers copy, to avoid copy as much as possible +func (c *RouterChain) copyInvokerIfNecessary(cache *InvokerCache) []protocol.Invoker { + var invokers []protocol.Invoker + if cache != nil { + invokers = cache.invokers + } + + c.mutex.RLock() + defer c.mutex.RUnlock() + if isInvokersChanged(invokers, c.invokers) { + invokers = c.copyInvokers() + } + return invokers +} + +// buildCache builds address cache with the new invokers for all poolable routers. +func (c *RouterChain) buildCache() { + origin := c.loadCache() + invokers := c.copyInvokerIfNecessary(origin) + if invokers == nil || len(invokers) == 0 { + return + } + + var ( + mutex sync.Mutex + wg sync.WaitGroup + ) + + cache := BuildCache(invokers) + for _, r := range c.copyRouters() { + if p, ok := r.(router.Poolable); ok { + wg.Add(1) + go func(p router.Poolable) { + defer wg.Done() + pool, info := poolRouter(p, origin, invokers) + mutex.Lock() + defer mutex.Unlock() + cache.pools[p.Name()] = pool + cache.metadatas[p.Name()] = info + }(p) + } + } + wg.Wait() + + c.cache.Store(cache) +} + // URL Return URL in RouterChain func (c *RouterChain) URL() common.URL { return c.url @@ -118,14 +244,62 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { chain := &RouterChain{ builtinRouters: routers, routers: newRouters, + last: time.Now(), + notify: make(chan struct{}), } if url != nil { chain.url = *url } + go chain.loop() return chain, nil } +// poolRouter calls poolable router's Pool() to create new address pool and address metadata if necessary. +// If the corresponding cache entry exists, and the poolable router answers no need to re-pool (possibly because its +// rule doesn't change), and the address list doesn't change, then the existing data will be re-used. +func poolRouter(p router.Poolable, origin *InvokerCache, invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + name := p.Name() + if isCacheMiss(origin, name) || p.ShouldPool() || &(origin.invokers) != &invokers { + logger.Debugf("build address cache for router %q", name) + return p.Pool(invokers) + } + + logger.Debugf("reuse existing address cache for router %q", name) + return origin.pools[name], origin.metadatas[name] +} + +// isCacheMiss checks if the corresponding cache entry for a poolable router has already existed. +// False returns when the cache is nil, or cache's pool is nil, or cache's invokers snapshot is nil, or the entry +// doesn't exist. +func isCacheMiss(cache *InvokerCache, key string) bool { + if cache == nil || cache.pools == nil || cache.invokers == nil || cache.pools[key] == nil { + return true + } + return false +} + +// isInvokersChanged compares new invokers on the right changes, compared with the old invokers on the left. +func isInvokersChanged(left []protocol.Invoker, right []protocol.Invoker) bool { + if len(right) != len(left) { + return true + } + + for _, r := range right { + found := false + for _, l := range left { + if common.IsEquals(l.GetUrl(), r.GetUrl(), constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY) { + found = true + break + } + } + if !found { + return true + } + } + return false +} + // sortRouter Sort router instance by priority with stable algorithm func sortRouter(routers []router.PriorityRouter) { sort.Stable(byPriority(routers)) diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go index dec03894ebc73e315c2bb161911bdc67235e1ebb..5a9f2d1c74db847df56b1ebab609b65c0cbd7484 100644 --- a/cluster/router/chain/chain_test.go +++ b/cluster/router/chain/chain_test.go @@ -172,12 +172,12 @@ func TestRouterChainRoute(t *testing.T) { invokers := []protocol.Invoker{} dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) - chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(&targetURL, inv) assert.Equal(t, 1, len(finalInvokers)) } @@ -213,10 +213,12 @@ conditions: invokers := []protocol.Invoker{} dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) + chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(&targetURL, inv) assert.Equal(t, 0, len(finalInvokers)) } @@ -236,13 +238,16 @@ func TestRouterChainRouteNoRoute(t *testing.T) { url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) + invokers := []protocol.Invoker{} dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) + chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(&targetURL, inv) assert.Equal(t, 0, len(finalInvokers)) } diff --git a/cluster/router/chain/invoker_cache.go b/cluster/router/chain/invoker_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..43cdfa50670f5f09666dbea10a2837d58cafd1b0 --- /dev/null +++ b/cluster/router/chain/invoker_cache.go @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package chain + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" + "github.com/apache/dubbo-go/protocol" +) + +// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received +// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if +// the router implements Poolable. +type InvokerCache struct { + // The snapshot of invokers + invokers []protocol.Invoker + + // The bitmap representation for invokers snapshot + bitmap *roaring.Bitmap + + // Address pool from routers which implement Poolable + pools map[string]router.AddrPool + + // Address metadata from routers which implement Poolable + metadatas map[string]router.AddrMetadata +} + +// BuildCache builds address cache from the given invokers. +func BuildCache(invokers []protocol.Invoker) *InvokerCache { + return &InvokerCache{ + invokers: invokers, + bitmap: utils.ToBitmap(invokers), + pools: make(map[string]router.AddrPool, 8), + metadatas: make(map[string]router.AddrMetadata, 8), + } +} + +// GetInvokers get invokers snapshot. +func (c *InvokerCache) GetInvokers() []protocol.Invoker { + return c.invokers +} + +// FindAddrPool finds address pool for a poolable router. +func (c *InvokerCache) FindAddrPool(p router.Poolable) router.AddrPool { + return c.pools[p.Name()] +} + +// FindAddrMeta finds address metadata for a poolable router. +func (c *InvokerCache) FindAddrMeta(p router.Poolable) router.AddrMetadata { + return c.metadatas[p.Name()] +} + +// SetAddrPool sets address pool for a poolable router, for unit test only +func (c *InvokerCache) SetAddrPool(name string, pool router.AddrPool) { + c.pools[name] = pool +} + +// SetAddrMeta sets address metadata for a poolable router, for unit test only +func (c *InvokerCache) SetAddrMeta(name string, meta router.AddrMetadata) { + c.metadatas[name] = meta +} diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go index 0f61b39fc71af3aaeffc731974a0fa997503693e..b31f6c7a82ea102c6e0b80d68e43f7b359785544 100644 --- a/cluster/router/condition/factory_test.go +++ b/cluster/router/condition/factory_test.go @@ -32,6 +32,9 @@ import ( ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" @@ -180,30 +183,30 @@ func TestRoute_matchFilter(t *testing.T) { router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5)) router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6)) cUrl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers4 := router4.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers5 := router5.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers6 := router6.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - assert.Equal(t, 1, len(fileredInvokers1)) - assert.Equal(t, 0, len(fileredInvokers2)) - assert.Equal(t, 0, len(fileredInvokers3)) - assert.Equal(t, 1, len(fileredInvokers4)) - assert.Equal(t, 2, len(fileredInvokers5)) - assert.Equal(t, 1, len(fileredInvokers6)) + ret1 := router1.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + ret2 := router2.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + ret3 := router3.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + ret4 := router4.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + ret5 := router5.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + ret6 := router6.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &cUrl, &invocation.RPCInvocation{}) + assert.Equal(t, 1, len(ret1.ToArray())) + assert.Equal(t, 0, len(ret2.ToArray())) + assert.Equal(t, 0, len(ret3.ToArray())) + assert.Equal(t, 1, len(ret4.ToArray())) + assert.Equal(t, 2, len(ret5.ToArray())) + assert.Equal(t, 1, len(ret6.ToArray())) } func TestRoute_methodRoute(t *testing.T) { inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{})) rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo") - matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv) + matchWhen := r.(*ConditionRouter).MatchWhen(&url, inv) assert.Equal(t, true, matchWhen) url1, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) - matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv) + matchWhen = r.(*ConditionRouter).MatchWhen(&url1, inv) assert.Equal(t, true, matchWhen) url2, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4")) @@ -225,9 +228,9 @@ func TestRoute_ReturnFalse(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 0, len(ret.ToArray())) } func TestRoute_ReturnEmpty(t *testing.T) { @@ -237,9 +240,9 @@ func TestRoute_ReturnEmpty(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => ")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 0, len(ret.ToArray())) } func TestRoute_ReturnAll(t *testing.T) { @@ -253,9 +256,9 @@ func TestRoute_ReturnAll(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, invokers, fileredInvokers) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, len(invokers), len(ret.ToArray())) } func TestRoute_HostFilter(t *testing.T) { @@ -270,11 +273,11 @@ func TestRoute_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_Empty_HostFilter(t *testing.T) { @@ -289,11 +292,11 @@ func TestRoute_Empty_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_False_HostFilter(t *testing.T) { @@ -308,11 +311,11 @@ func TestRoute_False_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_Placeholder(t *testing.T) { @@ -327,11 +330,11 @@ func TestRoute_Placeholder(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_NoForce(t *testing.T) { @@ -346,9 +349,9 @@ func TestRoute_NoForce(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, invokers, fileredInvokers) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule)) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, len(invokers), len(ret.ToArray())) } func TestRoute_Force(t *testing.T) { @@ -363,9 +366,9 @@ func TestRoute_Force(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true")) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true")) + fileredInvokers := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), &curl, inv) + assert.Equal(t, 0, len(fileredInvokers.ToArray())) } func TestNewConditionRouterFactory(t *testing.T) { @@ -377,3 +380,7 @@ func TestNewAppRouterFactory(t *testing.T) { factory := newAppRouterFactory() assert.NotNil(t, factory) } + +func setUpAddrCache(addrs []protocol.Invoker) router.Cache { + return chain.BuildCache(addrs) +} diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go index 7f4f14a8e47173253e2e5b7f4eed5db2bed64958..19e3a00bf6f592a14c867ae580946bd3b94f4bea 100644 --- a/cluster/router/condition/listenable_router.go +++ b/cluster/router/condition/listenable_router.go @@ -22,10 +22,12 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/constant" @@ -64,14 +66,14 @@ func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) { l.priority = listenableRouterDefaultPriority routerKey := ruleKey + constant.ConditionRouterRuleSuffix - //add listener + // add listener dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration() if dynamicConfiguration == nil { return nil, perrors.Errorf("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please") } dynamicConfiguration.AddListener(routerKey, l) - //get rule + // get rule rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP)) if len(rule) == 0 || err != nil { return nil, perrors.Errorf("Get rule fail, config rule{%s}, error{%v}", rule, err) @@ -129,13 +131,13 @@ func (l *listenableRouter) generateConditions(rule *RouterRule) { } // Route Determine the target invokers list. -func (l *listenableRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if len(invokers) == 0 || len(l.conditionRouters) == 0 { +func (l *listenableRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if invokers.IsEmpty() || len(l.conditionRouters) == 0 { return invokers } - //We will check enabled status inside each router. + // We will check enabled status inside each router. for _, r := range l.conditionRouters { - invokers = r.Route(invokers, url, invocation) + invokers = r.Route(invokers, cache, url, invocation) } return invokers } diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go index 751b5a7111655577566c561614d39093485130cd..4267f5b405367950d38c5d7af705e663f4416602 100644 --- a/cluster/router/condition/router.go +++ b/cluster/router/condition/router.go @@ -23,20 +23,23 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" + "github.com/dubbogo/gost/container/set" + "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" - "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/net" ) const ( - //pattern route pattern regex + // pattern route pattern regex pattern = `([&!=,]*)\\s*([^&!=,\\s]+)` ) @@ -148,29 +151,36 @@ func (c *ConditionRouter) Enabled() bool { } // Route Determine the target invokers list. -func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { +func (c *ConditionRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { if !c.Enabled() { return invokers } - if len(invokers) == 0 { + + if invokers.IsEmpty() { return invokers } + isMatchWhen := c.MatchWhen(url, invocation) if !isMatchWhen { return invokers } - var result []protocol.Invoker + if len(c.ThenCondition) == 0 { - return result + return utils.EmptyAddr } - for _, invoker := range invokers { + + result := roaring.NewBitmap() + for iter := invokers.Iterator(); iter.HasNext(); { + index := iter.Next() + invoker := cache.GetInvokers()[index] invokerUrl := invoker.GetUrl() isMatchThen := c.MatchThen(&invokerUrl, url) if isMatchThen { - result = append(result, invoker) + result.Add(index) } } - if len(result) > 0 { + + if !result.IsEmpty() { return result } else if c.Force { rule, _ := url.GetParamAndDecoded(constant.RULE_KEY) @@ -178,6 +188,7 @@ func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, in logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule) return result } + return invokers } diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go index ee42e47e3b26c9a1976b4599d3464d752b615e0a..75ad189967ed75b9f6153640f49f29217543ede7 100644 --- a/cluster/router/healthcheck/health_check_route.go +++ b/cluster/router/healthcheck/health_check_route.go @@ -17,8 +17,13 @@ package healthcheck +import ( + "github.com/RoaringBitmap/roaring" +) + import ( "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -28,6 +33,8 @@ import ( const ( HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled" + healthy = "healthy" + name = "health-check-router" ) // HealthCheckRouter provides a health-first routing mechanism through HealthChecker @@ -51,25 +58,48 @@ func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) { } // Route gets a list of healthy invoker -func (r *HealthCheckRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { +func (r *HealthCheckRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { if !r.enabled { return invokers } - healthyInvokers := make([]protocol.Invoker, 0, len(invokers)) + + addrPool := cache.FindAddrPool(r) // Add healthy invoker to the list - for _, invoker := range invokers { - if r.checker.IsHealthy(invoker) { - healthyInvokers = append(healthyInvokers, invoker) - } - } - // If all Invoke are considered unhealthy, downgrade to all inovker - if len(healthyInvokers) == 0 { + healthyInvokers := utils.JoinIfNotEqual(addrPool[healthy], invokers) + // If all invokers are considered unhealthy, downgrade to all invoker + if healthyInvokers.IsEmpty() { logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey()) return invokers } return healthyInvokers } +// Pool separates healthy invokers from others. +func (r *HealthCheckRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + if !r.enabled { + return nil, nil + } + + rb := make(router.AddrPool, 8) + rb[healthy] = roaring.NewBitmap() + for i, invoker := range invokers { + if r.checker.IsHealthy(invoker) { + rb[healthy].Add(uint32(i)) + } + } + + return rb, nil +} + +// ShouldPool will always return true to make sure healthy check constantly. +func (r *HealthCheckRouter) ShouldPool() bool { + return r.enabled +} + +func (r *HealthCheckRouter) Name() string { + return name +} + // Priority func (r *HealthCheckRouter) Priority() int64 { return 0 diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go index d5862fb884114bac0ea2ec9ee8926baac57d5ba6..c321b56156b9ae272048f32ef50eb419add27b03 100644 --- a/cluster/router/healthcheck/health_check_route_test.go +++ b/cluster/router/healthcheck/health_check_route_test.go @@ -29,6 +29,9 @@ import ( ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" @@ -59,25 +62,25 @@ func TestHealthCheckRouterRoute(t *testing.T) { invoker3 := NewMockInvoker(url3) invokers = append(invokers, invoker1, invoker2, invoker3) inv := invocation.NewRPCInvocation(healthCheckRouteMethodNameTest, nil, nil) - res := hcr.Route(invokers, &consumerURL, inv) + res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) // now all invokers are healthy - assert.True(t, len(res) == len(invokers)) + assert.True(t, len(res.ToArray()) == len(invokers)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) // invokers1 is unhealthy now - assert.True(t, len(res) == 2 && !contains(res, invoker1)) + assert.True(t, len(res.ToArray()) == 2 && !res.Contains(0)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) request(url2, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) // only invokers3 is healthy now - assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2)) + assert.True(t, len(res.ToArray()) == 1 && !res.Contains(0) && !res.Contains(1)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) @@ -85,37 +88,28 @@ func TestHealthCheckRouterRoute(t *testing.T) { request(url3, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) // now all invokers are unhealthy, so downgraded to all - assert.True(t, len(res) == 3) + assert.True(t, len(res.ToArray()) == 3) // reset the invoker1 successive failed count, so invoker1 go to healthy request(url1, healthCheckRouteMethodNameTest, 0, false, true) - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, contains(res, invoker1)) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) + assert.True(t, res.Contains(0)) for i := 0; i < 6; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) } // now all invokers are unhealthy, so downgraded to all again - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, len(res) == 3) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) + assert.True(t, len(res.ToArray()) == 3) time.Sleep(time.Second * 2) // invoker1 go to healthy again after 2s - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, contains(res, invoker1)) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), &consumerURL, inv) + assert.True(t, res.Contains(0)) } -func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool { - for _, e := range invokers { - if e == invoker { - return true - } - } - return false -} - func TestNewHealthCheckRouter(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) @@ -143,3 +137,11 @@ func TestNewHealthCheckRouter(t *testing.T) { assert.Equal(t, dhc.requestSuccessiveFailureThreshold, int32(10)) assert.Equal(t, dhc.circuitTrippedTimeoutFactor, int32(500)) } + +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrMeta(r.Name(), info) + cache.SetAddrPool(r.Name(), pool) + return cache +} diff --git a/cluster/router/router.go b/cluster/router/router.go index 66603c1d4d0efedad3489712ecea91b43254fffd..ddca42a01d5d5b56a33b9145926c77a843ea9787 100644 --- a/cluster/router/router.go +++ b/cluster/router/router.go @@ -17,6 +17,10 @@ package router +import ( + "github.com/RoaringBitmap/roaring" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/protocol" @@ -38,7 +42,8 @@ type FilePriorityRouterFactory interface { // Router type router interface { // Route Determine the target invokers list. - Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker + Route(*roaring.Bitmap, Cache, *common.URL, protocol.Invocation) *roaring.Bitmap + // URL Return URL in router URL() common.URL } @@ -51,10 +56,38 @@ type PriorityRouter interface { Priority() int64 } -// NotifyRouter notify router use the invoker list. Invoker list may change from time to time. This method gives the router a -// chance to prepare before {@link Router#route(List, URL, Invocation)} gets called. -type NotifyRouter interface { - PriorityRouter - // Notify notify whenever addresses in registry change - Notify([]protocol.Invoker) +// Poolable caches address pool and address metadata for a router instance which will be used later in Router's Route. +type Poolable interface { + // Pool created address pool and address metadata from the invokers. + Pool([]protocol.Invoker) (AddrPool, AddrMetadata) + + // ShouldPool returns if it should pool. One typical scenario is a router rule changes, in this case, a pooling + // is necessary, even if the addresses not changed at all. + ShouldPool() bool + + // Name return the Poolable's name. + Name() string +} + +// AddrPool is an address pool, backed by a snapshot of address list, divided into categories. +type AddrPool map[string]*roaring.Bitmap + +// AddrMetadta is address metadata, collected from a snapshot of address list by a router, if it implements Poolable. +type AddrMetadata interface { + // Source indicates where the metadata comes from. + Source() string +} + +// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received +// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if +// the router implements Poolable. +type Cache interface { + // GetInvokers returns the snapshot of received invokers. + GetInvokers() []protocol.Invoker + + // FindAddrPool returns address pool associated with the given Poolable instance. + FindAddrPool(Poolable) AddrPool + + // FindAddrMeta returns address metadata associated with the given Poolable instance. + FindAddrMeta(Poolable) AddrMetadata } diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go index 433abcb72eb6e201e64790af932e847d8faba8af..0ee43116474905d17328364a40889baef0f8c0d4 100644 --- a/cluster/router/tag/file.go +++ b/cluster/router/tag/file.go @@ -24,10 +24,12 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" @@ -74,9 +76,10 @@ func (f *FileTagRouter) Priority() int64 { return f.router.priority } -func (f *FileTagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if len(invokers) == 0 { +func (f *FileTagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if invokers.IsEmpty() { return invokers } - return f.Route(invokers, url, invocation) + // FIXME: I believe this is incorrect. + return f.Route(invokers, cache, url, invocation) } diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go index b35d455a6e763056b5d52da7f494258a22fd4b8c..c0a2d763ae7396ae41694f558f443d46084c3357 100644 --- a/cluster/router/tag/router_rule.go +++ b/cluster/router/tag/router_rule.go @@ -41,8 +41,8 @@ import ( type RouterRule struct { router.BaseRouterRule `yaml:",inline"` Tags []Tag - addressToTagNames map[string][]string - tagNameToAddresses map[string][]string + AddressToTagNames map[string][]string + TagNameToAddresses map[string][]string } func getRule(rawRule string) (*RouterRule, error) { @@ -58,13 +58,13 @@ func getRule(rawRule string) (*RouterRule, error) { // parseTags use for flattening tags data to @addressToTagNames and @tagNameToAddresses func (t *RouterRule) parseTags() { - t.addressToTagNames = make(map[string][]string, 2*len(t.Tags)) - t.tagNameToAddresses = make(map[string][]string, len(t.Tags)) + t.AddressToTagNames = make(map[string][]string, 2*len(t.Tags)) + t.TagNameToAddresses = make(map[string][]string, len(t.Tags)) for _, tag := range t.Tags { for _, address := range tag.Addresses { - t.addressToTagNames[address] = append(t.addressToTagNames[address], tag.Name) + t.AddressToTagNames[address] = append(t.AddressToTagNames[address], tag.Name) } - t.tagNameToAddresses[tag.Name] = tag.Addresses + t.TagNameToAddresses[tag.Name] = tag.Addresses } } @@ -85,15 +85,15 @@ func (t *RouterRule) getTagNames() []string { } func (t *RouterRule) hasTag(tag string) bool { - return len(t.tagNameToAddresses[tag]) > 0 + return len(t.TagNameToAddresses[tag]) > 0 } func (t *RouterRule) getAddressToTagNames() map[string][]string { - return t.addressToTagNames + return t.AddressToTagNames } func (t *RouterRule) getTagNameToAddresses() map[string][]string { - return t.tagNameToAddresses + return t.TagNameToAddresses } func (t *RouterRule) getTags() []Tag { diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go index bd5c359524e005a48d593a353118bc0a2c10b3f8..0acdf70ce97b7e1984d32ae9d4fc1b50d97ba071 100644 --- a/cluster/router/tag/tag_router.go +++ b/cluster/router/tag/tag_router.go @@ -18,16 +18,20 @@ package tag import ( - "net" "strconv" + "strings" + "sync" ) import ( + "github.com/RoaringBitmap/roaring" gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/constant" @@ -37,6 +41,32 @@ import ( "github.com/apache/dubbo-go/remoting" ) +const ( + name = "tag-router" + staticPrefix = "static-" + dynamicPrefix = "dynamic-" +) + +// addrMetadata keeps snapshot data for app name, and some infos extracted from dynamic tag rule in order to make +// Route() method lock-free. +type addrMetadata struct { + // application name + application string + // is rule a runtime rule + ruleRuntime bool + // is rule a force rule + ruleForce bool + // is rule a valid rule + ruleValid bool + // is rule an enabled rule + ruleEnabled bool +} + +// Source indicates where the metadata comes from. +func (m *addrMetadata) Source() string { + return name +} + // tagRouter defines url, enable and the priority type tagRouter struct { url *common.URL @@ -44,6 +74,8 @@ type tagRouter struct { enabled bool priority int64 application string + ruleChanged bool + mutex sync.RWMutex } // NewTagRouter returns a tagRouter instance if url is not nil @@ -63,61 +95,80 @@ func (c *tagRouter) isEnabled() bool { return c.enabled } -func (c *tagRouter) tagRouterRuleCopy() RouterRule { - routerRule := *c.tagRouterRule - return routerRule -} - // Route gets a list of invoker -func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - var ( - result []protocol.Invoker - addresses []string - ) +func (c *tagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if !c.isEnabled() || invokers.IsEmpty() { + return invokers + } + + if shouldUseDynamicTag(cache.FindAddrMeta(c)) { + return c.routeWithDynamicTag(invokers, cache, url, invocation) + } + return c.routeWithStaticTag(invokers, cache, url, invocation) +} - if !c.isEnabled() || len(invokers) == 0 { +// routeWithStaticTag routes with static tag rule +func (c *tagRouter) routeWithStaticTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + tag := findTag(invocation, url) + if tag == "" { return invokers } - // Use static tags if dynamic tags are not set or invalid - if c.tagRouterRule == nil || !c.tagRouterRule.Valid || !c.tagRouterRule.Enabled { - return filterUsingStaticTag(invokers, url, invocation) + ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag) + if ret.IsEmpty() && !isForceUseTag(url, invocation) { + return invokers } - // since the rule can be changed by config center, we should copy one to use. - tagRouterRuleCopy := c.tagRouterRuleCopy() - tag, ok := invocation.Attachments()[constant.Tagkey] - if !ok { - tag = url.GetParam(constant.Tagkey, "") + return ret +} + +// routeWithDynamicTag routes with dynamic tag rule +func (c *tagRouter) routeWithDynamicTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + tag := findTag(invocation, url) + if tag == "" { + return c.filterNotInDynamicTag(invokers, cache) } - // if we are requesting for a Provider with a specific tag - if len(tag) > 0 { - return filterInvokersWithTag(invokers, url, invocation, tagRouterRuleCopy, tag) + ret, ok := c.filterWithTag(invokers, cache, dynamicPrefix+tag) + if ok && (!ret.IsEmpty() || isTagRuleForce(cache.FindAddrMeta(c))) { + return ret } - // return all addresses in dynamic tag group. - addresses = tagRouterRuleCopy.getAddresses() - if len(addresses) > 0 { - filterAddressNotMatches := func(invoker protocol.Invoker) bool { - url := invoker.GetUrl() - return len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port) - } - result = filterInvoker(invokers, filterAddressNotMatches) - // 1. all addresses are in dynamic tag group, return empty list. - if len(result) == 0 { - return result + // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by + // dynamic tag group but force=false. check static tag + if ret.IsEmpty() { + ret, _ = c.filterWithTag(invokers, cache, staticPrefix+tag) + // If there's no tagged providers that can match the current tagged request. force.tag is set by default + // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. + if !ret.IsEmpty() || isForceUseTag(url, invocation) { + return ret } + return c.filterNotInDynamicTag(invokers, cache) + } + return ret +} + +// filterWithTag filters incoming invokers with the given tag +func (c *tagRouter) filterWithTag(invokers *roaring.Bitmap, cache router.Cache, tag string) (*roaring.Bitmap, bool) { + if target, ok := cache.FindAddrPool(c)[tag]; ok { + return utils.JoinIfNotEqual(target, invokers), true } - // 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the - // static tag group. - filter := func(invoker protocol.Invoker) bool { - localTag := invoker.GetUrl().GetParam(constant.Tagkey, "") - return len(localTag) == 0 || !(tagRouterRuleCopy.hasTag(localTag)) + return utils.EmptyAddr, false +} + +// filterNotInDynamicTag filters incoming invokers not applied to dynamic tag rule +func (c *tagRouter) filterNotInDynamicTag(invokers *roaring.Bitmap, cache router.Cache) *roaring.Bitmap { + // FAILOVER: return all Providers without any tags. + invokers = invokers.Clone() + for k, v := range cache.FindAddrPool(c) { + if strings.HasPrefix(k, dynamicPrefix) { + invokers.AndNot(v) + } } - return filterInvoker(result, filter) + return invokers } +// Process parses dynamic tag rule func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) { logger.Infof("Notification of tag rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value) if remoting.EventTypeDel == event.ConfigType { @@ -135,16 +186,53 @@ func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) { logger.Errorf("Parse dynamic tag router rule fail,error:[%s] ", err) return } + + c.mutex.Lock() + defer c.mutex.Unlock() c.tagRouterRule = routerRule + c.ruleChanged = true return } -func (c *tagRouter) Notify(invokers []protocol.Invoker) { - if len(invokers) == 0 { +// URL gets the url of tagRouter +func (c *tagRouter) URL() common.URL { + return *c.url +} + +// Priority gets the priority of tagRouter +func (c *tagRouter) Priority() int64 { + return c.priority +} + +// Pool divided invokers into different address pool by tag. +func (c *tagRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + c.fetchRuleIfNecessary(invokers) + + rb := make(router.AddrPool, 8) + poolWithStaticTag(invokers, rb) + + c.mutex.Lock() + defer c.mutex.Unlock() + poolWithDynamicTag(invokers, c.tagRouterRule, rb) + c.ruleChanged = false + // create metadata in order to avoid lock in route() + meta := addrMetadata{application: c.application} + if c.tagRouterRule != nil { + meta.ruleForce = c.tagRouterRule.Force + meta.ruleEnabled = c.tagRouterRule.Enabled + meta.ruleValid = c.tagRouterRule.Valid + } + + return rb, &meta +} + +// fetchRuleIfNecessary fetches, parses rule and register listener for the further change +func (c *tagRouter) fetchRuleIfNecessary(invokers []protocol.Invoker) { + if invokers == nil || len(invokers) == 0 { return } - invoker := invokers[0] - url := invoker.GetUrl() + + url := invokers[0].GetUrl() providerApplication := url.GetParam(constant.RemoteApplicationKey, "") if len(providerApplication) == 0 { logger.Error("TagRouter must getConfig from or subscribe to a specific application, but the application " + @@ -159,11 +247,15 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) { if providerApplication != c.application { dynamicConfiguration.RemoveListener(c.application+constant.TagRouterRuleSuffix, c) + } else { + // if app name from URL is as same as the current app name, then it is safe to jump out + return } + c.application = providerApplication routerKey := providerApplication + constant.TagRouterRuleSuffix dynamicConfiguration.AddListener(routerKey, c) - //get rule + // get rule rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP)) if len(rule) == 0 || err != nil { logger.Errorf("Get rule fail, config rule{%s}, error{%v}", rule, err) @@ -177,71 +269,52 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) { } } -// URL gets the url of tagRouter -func (c *tagRouter) URL() common.URL { - return *c.url +// ShouldPool returns false, to make sure address cache for tag router happens once and only once. +func (c *tagRouter) ShouldPool() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.ruleChanged } -// Priority gets the priority of tagRouter -func (c *tagRouter) Priority() int64 { - return c.priority +// Name returns pool's name +func (c *tagRouter) Name() string { + return name } -// filterUsingStaticTag gets a list of invoker using static tag -func filterUsingStaticTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if tag, ok := invocation.Attachments()[constant.Tagkey]; ok { - result := make([]protocol.Invoker, 0, 8) - for _, v := range invokers { - if v.GetUrl().GetParam(constant.Tagkey, "") == tag { - result = append(result, v) - } - } - if len(result) == 0 && !isForceUseTag(url, invocation) { - return invokers - } - return result +// poolWithDynamicTag pools addresses with the tags defined in dynamic tag rule, all keys have prefix "dynamic-" +func poolWithDynamicTag(invokers []protocol.Invoker, rule *RouterRule, pool router.AddrPool) { + if rule == nil { + return } - return invokers -} -// filterInvokersWithTag gets a list of invoker using dynamic route with tag -func filterInvokersWithTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation, tagRouterRule RouterRule, tag string) []protocol.Invoker { - var ( - result []protocol.Invoker - addresses []string - ) - addresses, _ = tagRouterRule.getTagNameToAddresses()[tag] - // filter by dynamic tag group first - if len(addresses) > 0 { - filterAddressMatches := func(invoker protocol.Invoker) bool { - url := invoker.GetUrl() - return len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port) - } - result = filterInvoker(invokers, filterAddressMatches) - if len(result) > 0 || tagRouterRule.Force { - return result - } - } - // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by - // dynamic tag group but force=false. check static tag - filter := func(invoker protocol.Invoker) bool { - return invoker.GetUrl().GetParam(constant.Tagkey, "") == tag - } - result = filterInvoker(invokers, filter) - // If there's no tagged providers that can match the current tagged request. force.tag is set by default - // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. - if len(result) > 0 || isForceUseTag(url, invocation) { - return result + tagNameToAddresses := rule.getTagNameToAddresses() + for tag, addrs := range tagNameToAddresses { + pool[dynamicPrefix+tag] = addrsToBitmap(addrs, invokers) } - // FAILOVER: return all Providers without any tags. - filterAddressNotMatches := func(invoker protocol.Invoker) bool { +} + +// poolWithStaticTag pools addresses with tags found from incoming URLs, all keys have prefix "static-" +func poolWithStaticTag(invokers []protocol.Invoker, pool router.AddrPool) { + for i, invoker := range invokers { url := invoker.GetUrl() - return len(addresses) == 0 || !checkAddressMatch(tagRouterRule.getAddresses(), url.Ip, url.Port) - } - filterTagIsEmpty := func(invoker protocol.Invoker) bool { - return invoker.GetUrl().GetParam(constant.Tagkey, "") == "" + tag := url.GetParam(constant.Tagkey, "") + if len(tag) > 0 { + if _, ok := pool[staticPrefix+tag]; !ok { + pool[staticPrefix+tag] = roaring.NewBitmap() + } + pool[staticPrefix+tag].AddInt(i) + } } - return filterInvoker(invokers, filterAddressNotMatches, filterTagIsEmpty) +} + +// shouldUseDynamicTag uses the snapshot data from the parsed rule to decide if dynamic tag rule should be used or not +func shouldUseDynamicTag(meta router.AddrMetadata) bool { + return meta.(*addrMetadata).ruleValid && meta.(*addrMetadata).ruleEnabled +} + +// isTagRuleForce uses the snapshot data from the parsed rule to decide if dynamic tag rule is forced or not +func isTagRuleForce(meta router.AddrMetadata) bool { + return meta.(*addrMetadata).ruleForce } // isForceUseTag returns whether force use tag @@ -252,31 +325,44 @@ func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool { return false } -type filter func(protocol.Invoker) bool +// addrsToBitmap finds indexes for the given IP addresses in the target URL list, if any '0.0.0.0' IP address is met, +// then returns back all indexes of the URLs list. +func addrsToBitmap(addrs []string, invokers []protocol.Invoker) *roaring.Bitmap { + ret := roaring.NewBitmap() + for _, addr := range addrs { + if isAnyHost(addr) { + ret.AddRange(0, uint64(len(invokers))) + return ret + } -func filterInvoker(invokers []protocol.Invoker, filters ...filter) []protocol.Invoker { - var res []protocol.Invoker -OUTER: - for _, invoker := range invokers { - for _, filter := range filters { - if !filter(invoker) { - continue OUTER - } + index := findIndexWithIp(addr, invokers) + if index != -1 { + ret.AddInt(index) } - res = append(res, invoker) } - return res + return ret } -func checkAddressMatch(addresses []string, host, port string) bool { - addr := net.JoinHostPort(constant.ANYHOST_VALUE, port) - for _, address := range addresses { - if gxnet.MatchIP(address, host, port) { - return true - } - if address == addr { - return true +// findIndexWithIp finds index for one particular IP +func findIndexWithIp(addr string, invokers []protocol.Invoker) int { + for i, invoker := range invokers { + if gxnet.MatchIP(addr, invoker.GetUrl().Ip, invoker.GetUrl().Port) { + return i } } - return false + return -1 +} + +// isAnyHost checks if an IP is '0.0.0.0' +func isAnyHost(addr string) bool { + return strings.HasPrefix(addr, constant.ANYHOST_VALUE) +} + +// findTag finds tag, first from invocation's attachment, then from URL +func findTag(invocation protocol.Invocation, consumerUrl *common.URL) string { + tag, ok := invocation.Attachments()[constant.Tagkey] + if !ok { + tag = consumerUrl.GetParam(constant.Tagkey, "") + } + return tag } diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go index 6b9a5e08f133403565d8b2dd38e03a789c5e95e7..92611ac2c59538ea090763d91cb7c5a6ef44e8ee 100644 --- a/cluster/router/tag/tag_router_test.go +++ b/cluster/router/tag/tag_router_test.go @@ -25,15 +25,18 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" "github.com/dubbogo/go-zookeeper/zk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" - "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config_center" _ "github.com/apache/dubbo-go/config_center/zookeeper" @@ -143,17 +146,17 @@ func TestTagRouterRouteForce(t *testing.T) { invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) - invRst1 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 1, len(invRst1)) - assert.Equal(t, tagRouterTestHangZhou, invRst1[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) + invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 1, len(invRst1.ToArray())) + assert.Equal(t, tagRouterTestHangZhou, invokers[invRst1.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, "")) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) - invRst2 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 0, len(invRst2)) + invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 0, len(invRst2.ToArray())) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) - invRst3 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 3, len(invRst3)) + invRst3 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 3, len(invRst3.ToArray())) } func TestTagRouterRouteNoForce(t *testing.T) { @@ -175,20 +178,37 @@ func TestTagRouterRouteNoForce(t *testing.T) { invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) - invRst := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 1, len(invRst)) - assert.Equal(t, tagRouterTestHangZhou, invRst[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) + invRst := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 1, len(invRst.ToArray())) + assert.Equal(t, tagRouterTestHangZhou, invokers[invRst.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, "")) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestTrue) - invRst1 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 0, len(invRst1)) + invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 0, len(invRst1.ToArray())) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) - invRst2 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 3, len(invRst2)) + invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), &u1, inv) + assert.Equal(t, 3, len(invRst2.ToArray())) } -func TestFilterInvoker(t *testing.T) { +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrPool(r.Name(), pool) + cache.SetAddrMeta(r.Name(), info) + return cache +} + +func setUpAddrCacheWithRuleDisabled(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + info.(*addrMetadata).ruleEnabled = false + cache := chain.BuildCache(addrs) + cache.SetAddrPool(r.Name(), pool) + cache.SetAddrMeta(r.Name(), info) + return cache +} + +func TestRouteBeijingInvoker(t *testing.T) { u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) u3, e3 := common.NewURL(tagRouterTestShangHaiUrl) u4, e4 := common.NewURL(tagRouterTestBeijingUrl) @@ -203,23 +223,17 @@ func TestFilterInvoker(t *testing.T) { inv5 := NewMockInvoker(u5) var invokers []protocol.Invoker invokers = append(invokers, inv2, inv3, inv4, inv5) - filterTag := func(invoker protocol.Invoker) bool { - if invoker.GetUrl().GetParam(constant.Tagkey, "") == "beijing" { - return true - } - return false - } - res := filterInvoker(invokers, filterTag) - assert.Equal(t, []protocol.Invoker{inv4, inv5}, res) - flag := true - filterEnabled := func(invoker protocol.Invoker) bool { - if invoker.GetUrl().GetParamBool(constant.RouterEnabled, false) == flag { - return true - } - return false - } - res2 := filterInvoker(invokers, filterTag, filterEnabled) - assert.Equal(t, []protocol.Invoker{inv4}, res2) + + url, _ := common.NewURL(tagRouterTestBeijingUrl) + tagRouter, _ := NewTagRouter(&url) + + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(tagRouter, invokers) + inv := &invocation.RPCInvocation{} + res := tagRouter.Route(rb, cache, &url, inv) + // inv4 and inv5 + assert.Equal(t, []uint32{2, 3}, res.ToArray()) } type DynamicTagRouter struct { @@ -301,44 +315,51 @@ func (suite *DynamicTagRouter) TearDownTest() { func (suite *DynamicTagRouter) TestDynamicTagRouterSetByIPv4() { invokers := suite.invokers - suite.route.Notify(invokers) + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(suite.route, invokers) suite.NotNil(suite.route.tagRouterRule) consumer := &invocation.RPCInvocation{} consumer.SetAttachments(tagRouterTestDubboTag, "tag1") - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[0]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(0)) consumer.SetAttachments(tagRouterTestDubboTag, "tag3") - targetInvokers = suite.route.Route(invokers, suite.url, consumer) - suite.Equal(2, len(targetInvokers)) - suite.Equal(targetInvokers, []protocol.Invoker{suite.invokers[2], suite.invokers[3]}) + targetInvokers = suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(2), targetInvokers.GetCardinality()) + suite.True(targetInvokers.Contains(2) && targetInvokers.Contains(3)) } func (suite *DynamicTagRouter) TestDynamicTagRouterStaticTag() { invokers := suite.invokers + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCacheWithRuleDisabled(suite.route, invokers) consumer := &invocation.RPCInvocation{} consumer.SetAttachments(tagRouterTestDubboTag, "tag4") - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[3]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(3)) } // Teas no tag and return a address are not in dynamic tag group func (suite *DynamicTagRouter) TestDynamicTagRouterByNoTagAndAddressMatch() { invokers := suite.invokers - suite.route.Notify(invokers) + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(suite.route, invokers) suite.NotNil(suite.route.tagRouterRule) consumer := &invocation.RPCInvocation{} - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[4]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(4)) // test if there are some addresses that are not in any dynamic tag group, continue to filter using the static tag group. consumer.SetAttachments(tagRouterTestDubboTag, "tag5") - targetInvokers = suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[4]) + targetInvokers = suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(4)) } func TestProcess(t *testing.T) { diff --git a/cluster/router/utils/bitmap_util.go b/cluster/router/utils/bitmap_util.go new file mode 100644 index 0000000000000000000000000000000000000000..8b4ee5538f339276fb4c41ceefe4df3575723f15 --- /dev/null +++ b/cluster/router/utils/bitmap_util.go @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/protocol" +) + +var EmptyAddr = roaring.NewBitmap() + +func JoinIfNotEqual(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap { + if !left.Equals(right) { + left = left.Clone() + left.And(right) + } + return left +} + +func FallbackIfJoinToEmpty(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap { + ret := JoinIfNotEqual(left, right) + if ret == nil || ret.IsEmpty() { + return right + } + return ret +} + +func ToBitmap(invokers []protocol.Invoker) *roaring.Bitmap { + bitmap := roaring.NewBitmap() + bitmap.AddRange(0, uint64(len(invokers))) + return bitmap +} diff --git a/common/extension/metadata_service.go b/common/extension/metadata_service.go index 1823273b8f7f86b2d96abf990359e14b569abddb..e35677d148eee121c3a6c018a128b5d372c6f2c7 100644 --- a/common/extension/metadata_service.go +++ b/common/extension/metadata_service.go @@ -21,6 +21,10 @@ import ( "fmt" ) +import ( + perrors "github.com/pkg/errors" +) + import ( "github.com/apache/dubbo-go/metadata/service" ) @@ -36,12 +40,11 @@ func SetMetadataService(msType string, creator func() (service.MetadataService, } // GetMetadataService will create a MetadataService instance -// it will panic if msType not found func GetMetadataService(msType string) (service.MetadataService, error) { if creator, ok := metadataServiceInsMap[msType]; ok { return creator() } - panic(fmt.Sprintf("could not find the metadata service creator for metadataType: %s, please check whether you have imported relative packages, \n"+ + return nil, perrors.New(fmt.Sprintf("could not find the metadata service creator for metadataType: %s, please check whether you have imported relative packages, \n"+ "local - github.com/apache/dubbo-go/metadata/service/inmemory, \n"+ "remote - github.com/apache/dubbo-go/metadata/service/remote", msType)) } diff --git a/common/logger/logger.go b/common/logger/logger.go index 88ed0c29f897f13dabe2dd3979232b4e6c84b968..63eda231ddd174468602577d8b042bc0664700d0 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -25,7 +25,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" "go.uber.org/zap" "go.uber.org/zap/zapcore" @@ -60,6 +60,10 @@ type Logger interface { } func init() { + // forbidden to executing twice. + if logger != nil { + return + } logConfFile := os.Getenv(constant.APP_LOG_CONF_FILE) err := InitLog(logConfFile) if err != nil { diff --git a/common/url.go b/common/url.go index 176a95127ed0cdd3b11d48a45562f92ab37b4feb..b9d8b924c46bbec6dacb82e03f3b5ade40e7bb5c 100644 --- a/common/url.go +++ b/common/url.go @@ -647,6 +647,34 @@ func (c *URL) CloneWithParams(reserveParams []string) *URL { ) } +// IsEquals compares if two URLs equals with each other. Excludes are all parameter keys which should ignored. +func IsEquals(left URL, right URL, excludes ...string) bool { + if left.Ip != right.Ip || left.Port != right.Port { + return false + } + + leftMap := left.ToMap() + rightMap := right.ToMap() + for _, exclude := range excludes { + delete(leftMap, exclude) + delete(rightMap, exclude) + } + + if len(leftMap) != len(rightMap) { + return false + } + + for lk, lv := range leftMap { + if rv, ok := rightMap[lk]; !ok { + return false + } else if lv != rv { + return false + } + } + + return true +} + func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) { methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys)) for _, paramKey := range paramKeys { diff --git a/config/config_loader.go b/config/config_loader.go index 8b196305b9cf7d33cb149def149af178f0e2808c..75b82628d68a23e575cfa637b3603d09e09ea9d6 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -21,11 +21,14 @@ import ( "fmt" "log" "os" + "reflect" + "strconv" "sync" "time" ) import ( + gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) @@ -35,6 +38,7 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" _ "github.com/apache/dubbo-go/common/observer/dispatcher" + "github.com/apache/dubbo-go/registry" ) var ( @@ -206,6 +210,97 @@ func loadProviderConfig() { panic(fmt.Sprintf("service %s export failed! err: %#v", key, err)) } } + registerServiceInstance() +} + +// registerServiceInstance register service instance +func registerServiceInstance() { + url := selectMetadataServiceExportedURL() + if url == nil { + return + } + instance, err := createInstance(*url) + if err != nil { + panic(err) + } + p := extension.GetProtocol(constant.REGISTRY_KEY) + var rp registry.RegistryFactory + var ok bool + if rp, ok = p.(registry.RegistryFactory); !ok { + panic("dubbo registry protocol{" + reflect.TypeOf(p).String() + "} is invalid") + } + rs := rp.GetRegistries() + for _, r := range rs { + var sdr registry.ServiceDiscoveryHolder + if sdr, ok = r.(registry.ServiceDiscoveryHolder); !ok { + continue + } + err := sdr.GetServiceDiscovery().Register(instance) + if err != nil { + panic(err) + } + } +} + +// nolint +func createInstance(url common.URL) (registry.ServiceInstance, error) { + appConfig := GetApplicationConfig() + port, err := strconv.ParseInt(url.Port, 10, 32) + if err != nil { + return nil, perrors.WithMessage(err, "invalid port: "+url.Port) + } + + host := url.Ip + if len(host) == 0 { + host, err = gxnet.GetLocalIP() + if err != nil { + return nil, perrors.WithMessage(err, "could not get the local Ip") + } + } + + // usually we will add more metadata + metadata := make(map[string]string, 8) + metadata[constant.METADATA_STORAGE_TYPE_PROPERTY_NAME] = appConfig.MetadataType + + return ®istry.DefaultServiceInstance{ + ServiceName: appConfig.Name, + Host: host, + Port: int(port), + Id: host + constant.KEY_SEPARATOR + url.Port, + Enable: true, + Healthy: true, + Metadata: metadata, + }, nil +} + +// selectMetadataServiceExportedURL get already be exported url +func selectMetadataServiceExportedURL() *common.URL { + var selectedUrl common.URL + metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType) + if err != nil { + logger.Warn(err) + return nil + } + list, err := metaDataService.GetExportedURLs(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + if err != nil { + panic(err) + } + if len(list) == 0 { + return nil + } + for _, urlStr := range list { + url, err := common.NewURL(urlStr.(string)) + if err != nil { + logger.Errorf("url format error {%v}", url) + continue + } + selectedUrl = url + // rest first + if url.Protocol == "rest" { + break + } + } + return &selectedUrl } func initRouter() { diff --git a/config/config_loader_test.go b/config/config_loader_test.go index a219b9f465c71186e8cd482a7bbca32a03cdb396..461e607c1e0ad2e9471770224c8eb6d5f3ee96f6 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -19,10 +19,16 @@ package config import ( "path/filepath" + "sort" + "sync" "testing" ) import ( + cm "github.com/Workiva/go-datastructures/common" + "github.com/Workiva/go-datastructures/slice/skip" + gxset "github.com/dubbogo/gost/container/set" + gxpage "github.com/dubbogo/gost/page" "github.com/stretchr/testify/assert" "go.uber.org/atomic" ) @@ -33,8 +39,11 @@ import ( "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/metadata/service" + "github.com/apache/dubbo-go/registry" ) const mockConsumerConfigPath = "./testdata/consumer_config.yml" @@ -74,7 +83,17 @@ func TestLoad(t *testing.T) { extension.SetProtocol("registry", GetProtocol) extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster) extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory) - + GetApplicationConfig().MetadataType = "mock" + var mm *mockMetadataService + extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) { + if mm == nil { + mm = &mockMetadataService{ + exportedServiceURLs: new(sync.Map), + lock: new(sync.RWMutex), + } + } + return mm, nil + }) Load() assert.Equal(t, ms, GetRPCService(ms.Reference())) @@ -103,7 +122,17 @@ func TestLoadWithSingleReg(t *testing.T) { extension.SetProtocol("registry", GetProtocol) extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster) extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory) - + var mm *mockMetadataService + GetApplicationConfig().MetadataType = "mock" + extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) { + if mm == nil { + mm = &mockMetadataService{ + exportedServiceURLs: new(sync.Map), + lock: new(sync.RWMutex), + } + } + return mm, nil + }) Load() assert.Equal(t, ms, GetRPCService(ms.Reference())) @@ -132,7 +161,17 @@ func TestWithNoRegLoad(t *testing.T) { extension.SetProtocol("registry", GetProtocol) extension.SetCluster(constant.ZONEAWARE_CLUSTER_NAME, cluster_impl.NewZoneAwareCluster) extension.SetProxyFactory("default", proxy_factory.NewDefaultProxyFactory) - + var mm *mockMetadataService + GetApplicationConfig().MetadataType = "mock" + extension.SetMetadataService("mock", func() (metadataService service.MetadataService, err error) { + if mm == nil { + mm = &mockMetadataService{ + exportedServiceURLs: new(sync.Map), + lock: new(sync.RWMutex), + } + } + return mm, nil + }) Load() assert.Equal(t, ms, GetRPCService(ms.Reference())) @@ -300,3 +339,234 @@ func mockInitProviderWithSingleRegistry() { }, } } + +type mockMetadataService struct { + exportedServiceURLs *sync.Map + lock *sync.RWMutex +} + +func (m *mockMetadataService) Reference() string { + panic("implement me") +} + +func (m *mockMetadataService) ServiceName() (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { + return m.addURL(m.exportedServiceURLs, &url), nil +} + +func (m *mockMetadataService) UnexportURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { + return nil +} + +func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { + return ConvertURLArrToIntfArr(m.getAllService(m.exportedServiceURLs)), nil +} + +func (m *mockMetadataService) MethodMapper() map[string]string { + panic("implement me") +} + +func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { + panic("implement me") +} + +func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) Version() (string, error) { + panic("implement me") +} + +func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { + var ( + urlSet interface{} + loaded bool + ) + logger.Debug(url.ServiceKey()) + if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded { + mts.lock.RLock() + wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + if len(wantedUrl) > 0 && wantedUrl[0] != nil { + mts.lock.RUnlock() + return false + } + mts.lock.RUnlock() + } + mts.lock.Lock() + // double chk + wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + if len(wantedUrl) > 0 && wantedUrl[0] != nil { + mts.lock.Unlock() + return false + } + urlSet.(*skip.SkipList).Insert(Comparator(*url)) + mts.lock.Unlock() + return true +} + +func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL { + // using skip list to dedup and sorting + res := make([]common.URL, 0) + services.Range(func(key, value interface{}) bool { + urls := value.(*skip.SkipList) + for i := uint64(0); i < urls.Len(); i++ { + url := common.URL(urls.ByPosition(i).(Comparator)) + if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME { + res = append(res, url) + } + } + return true + }) + sort.Sort(common.URLSlice(res)) + return res +} + +type Comparator common.URL + +// Compare is defined as Comparator for skip list to compare the URL +func (c Comparator) Compare(comp cm.Comparator) int { + a := common.URL(c).String() + b := common.URL(comp.(Comparator)).String() + switch { + case a > b: + return 1 + case a < b: + return -1 + default: + return 0 + } +} + +type mockServiceDiscoveryRegistry struct { +} + +func (mr *mockServiceDiscoveryRegistry) GetUrl() common.URL { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) IsAvailable() bool { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) Destroy() { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) Register(url common.URL) error { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) UnRegister(url common.URL) error { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) Subscribe(*common.URL, registry.NotifyListener) error { + panic("implement me") +} + +func (mr *mockServiceDiscoveryRegistry) UnSubscribe(*common.URL, registry.NotifyListener) error { + panic("implement me") +} + +func (s *mockServiceDiscoveryRegistry) GetServiceDiscovery() registry.ServiceDiscovery { + return &mockServiceDiscovery{} +} + +type mockServiceDiscovery struct { +} + +func (m *mockServiceDiscovery) String() string { + panic("implement me") +} + +func (m *mockServiceDiscovery) Destroy() error { + panic("implement me") +} + +func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error { + return nil +} + +func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetDefaultPageSize() int { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetServices() *gxset.HashSet { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + panic("implement me") +} + +func ConvertURLArrToIntfArr(urls []common.URL) []interface{} { + if len(urls) == 0 { + return []interface{}{} + } + + res := make([]interface{}, 0, len(urls)) + for _, u := range urls { + res = append(res, u.String()) + } + return res +} diff --git a/config/consumer_config.go b/config/consumer_config.go index 177531209225b7a4b25751352d9777c0d4ee260c..9d283eeca7bbaf5a82f71357853c6b53560b2fe4 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -24,7 +24,6 @@ import ( import ( "github.com/creasty/defaults" - "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) @@ -34,6 +33,10 @@ import ( "github.com/apache/dubbo-go/common/yaml" ) +const ( + MaxWheelTimeSpan = 900e9 // 900s, 15 minute +) + ///////////////////////// // consumerConfig ///////////////////////// @@ -107,9 +110,9 @@ func ConsumerInit(confConFile string) error { if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil { return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout) } - if consumerConfig.RequestTimeout >= time.Duration(getty.MaxWheelTimeSpan) { + if consumerConfig.RequestTimeout >= time.Duration(MaxWheelTimeSpan) { return perrors.WithMessagef(err, "request_timeout %s should be less than %s", - consumerConfig.Request_Timeout, time.Duration(getty.MaxWheelTimeSpan)) + consumerConfig.Request_Timeout, time.Duration(MaxWheelTimeSpan)) } } if consumerConfig.Connect_Timeout != "" { diff --git a/config/reference_config_test.go b/config/reference_config_test.go index e45780159615c8896627b003fd45c35af86d3f02..a4345ad13d4944b8fae0930930584fccbd0fb33c 100644 --- a/config/reference_config_test.go +++ b/config/reference_config_test.go @@ -32,6 +32,7 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/registry" ) var regProtocol protocol.Protocol @@ -338,9 +339,37 @@ func (*mockRegistryProtocol) Refer(url common.URL) protocol.Invoker { } func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter { + registryUrl := getRegistryUrl(invoker) + if registryUrl.Protocol == "service-discovery" { + metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType) + if err != nil { + panic(err) + } + ok, err := metaDataService.ExportURL(*invoker.GetUrl().SubURL.Clone()) + if err != nil { + panic(err) + } + if !ok { + panic("The URL has been registry!") + } + } return protocol.NewBaseExporter("test", invoker, &sync.Map{}) } func (*mockRegistryProtocol) Destroy() { // Destroy is a mock function } +func getRegistryUrl(invoker protocol.Invoker) *common.URL { + // here add * for return a new url + url := invoker.GetUrl() + // if the protocol == registry ,set protocol the registry value in url.params + if url.Protocol == constant.REGISTRY_PROTOCOL { + protocol := url.GetParam(constant.REGISTRY_KEY, "") + url.Protocol = protocol + } + return &url +} + +func (p *mockRegistryProtocol) GetRegistries() []registry.Registry { + return []registry.Registry{&mockServiceDiscoveryRegistry{}} +} diff --git a/config/service_config_test.go b/config/service_config_test.go index d2bbda0c49ee18dfeb8e6475203f09d4c2fbb3b9..4d4122ee7057043af47aa0400ca8c5b5e9a20cd0 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -40,13 +40,33 @@ func doInitProvider() { Module: "module", Version: "2.6.0", Owner: "dubbo", - Environment: "test"}, + Environment: "test", + }, + Remotes: map[string]*RemoteConfig{ + "test1": { + Address: "127.0.0.5:2181", + TimeoutStr: "5s", + Username: "user1", + Password: "pwd1", + Params: nil, + }, + }, + ServiceDiscoveries: map[string]*ServiceDiscoveryConfig{ + "mock_servicediscovery": { + Protocol: "mock", + RemoteRef: "test1", + }, + }, + MetadataReportConfig: &MetadataReportConfig{ + Protocol: "mock", + RemoteRef: "test1", + }, }, Services: map[string]*ServiceConfig{ "MockService": { InterfaceName: "com.MockService", Protocol: "mock", - Registry: "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2", + Registry: "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2,hangzhou_service_discovery_reg", Cluster: "failover", Loadbalance: "random", Retries: "3", @@ -71,7 +91,7 @@ func doInitProvider() { "MockServiceNoRightProtocol": { InterfaceName: "com.MockService", Protocol: "mock1", - Registry: "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2", + Registry: "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2,hangzhou_service_discovery_reg", Cluster: "failover", Loadbalance: "random", Retries: "3", @@ -128,6 +148,14 @@ func doInitProvider() { Username: "user1", Password: "pwd1", }, + "hangzhou_service_discovery_reg": { + Protocol: "service-discovery", + Params: map[string]string{ + "service_discovery": "mock_servicediscovery", + "name_mapping": "in-memory", + "metadata": "default", + }, + }, }, Protocols: map[string]*ProtocolConfig{ diff --git a/config/ssl_config.go b/config/ssl_config.go index 019f83d3cbbc3191712f71fc4c6b6c9c972270b2..8576930367a487269bf82d51191bb02289dd1e73 100644 --- a/config/ssl_config.go +++ b/config/ssl_config.go @@ -18,7 +18,7 @@ package config import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" ) var ( diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index b049d334bca7e5191caaf9674734e731bc709ba2..8030a2c800c67d47a27e7aaa5d6f1bb39a83cdc9 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -27,7 +27,9 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" - "github.com/zouyx/agollo" + "github.com/zouyx/agollo/v3" + agolloConstant "github.com/zouyx/agollo/v3/constant" + "github.com/zouyx/agollo/v3/env/config" ) import ( @@ -35,19 +37,18 @@ import ( "github.com/apache/dubbo-go/common/constant" cc "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/config_center/parser" - "github.com/apache/dubbo-go/remoting" ) const ( apolloProtocolPrefix = "http://" - apolloConfigFormat = "%s.%s" + apolloConfigFormat = "%s%s" ) type apolloConfiguration struct { url *common.URL listeners sync.Map - appConf *agollo.AppConfig + appConf *config.AppConfig parser parser.ConfigurationParser } @@ -60,31 +61,20 @@ func newApolloConfiguration(url *common.URL) (*apolloConfiguration, error) { appId := url.GetParam(constant.CONFIG_APP_ID_KEY, "") namespaces := getProperties(url.GetParam(constant.CONFIG_NAMESPACE_KEY, cc.DEFAULT_GROUP)) - c.appConf = &agollo.AppConfig{ - AppId: appId, + c.appConf = &config.AppConfig{ + AppID: appId, Cluster: configCluster, NamespaceName: namespaces, - Ip: configAddr, + IP: configAddr, } - agollo.InitCustomConfig(func() (*agollo.AppConfig, error) { + agollo.InitCustomConfig(func() (*config.AppConfig, error) { return c.appConf, nil }) return c, agollo.Start() } -func getChangeType(change agollo.ConfigChangeType) remoting.EventType { - switch change { - case agollo.ADDED: - return remoting.EventTypeAdd - case agollo.DELETED: - return remoting.EventTypeDel - default: - return remoting.EventTypeUpdate - } -} - func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) { k := &cc.Options{} for _, opt := range opts { @@ -92,7 +82,7 @@ func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationL } key = k.Group + key - l, _ := c.listeners.LoadOrStore(key, NewApolloListener()) + l, _ := c.listeners.LoadOrStore(key, newApolloListener()) l.(*apolloListener).AddListener(listener) } @@ -110,10 +100,10 @@ func (c *apolloConfiguration) RemoveListener(key string, listener cc.Configurati } func getProperties(namespace string) string { - return getNamespaceName(namespace, agollo.Properties) + return getNamespaceName(namespace, agolloConstant.Properties) } -func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat) string { +func getNamespaceName(namespace string, configFileFormat agolloConstant.ConfigFileFormat) string { return fmt.Sprintf(apolloConfigFormat, namespace, configFileFormat) } @@ -148,7 +138,7 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri if config == nil { return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } - return config.GetContent(agollo.Properties), nil + return config.GetContent(), nil } func (c *apolloConfiguration) getAddressWithProtocolPrefix(url *common.URL) string { diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go index 335fb71045ca6349557cf7c736cead0565bdc193..50c4e689de4542e1c595d0778e2dce69b1e7dda9 100644 --- a/config_center/apollo/impl_test.go +++ b/config_center/apollo/impl_test.go @@ -202,9 +202,9 @@ func initMockApollo(t *testing.T) *apolloConfiguration { return configuration } -func TestAddListener(t *testing.T) { +func TestListener(t *testing.T) { listener := &apolloDataListener{} - listener.wg.Add(1) + listener.wg.Add(2) apollo := initMockApollo(t) mockConfigRes = `{ "appId": "testApplication_yang", @@ -215,28 +215,14 @@ func TestAddListener(t *testing.T) { }, "releaseKey": "20191104105242-0f13805d89f834a4" }` + //test add apollo.AddListener(mockNamespace, listener) listener.wg.Wait() - assert.Equal(t, "registries.hangzhouzk.username", listener.event) + assert.Equal(t, "mockDubbog.properties", listener.event) assert.Greater(t, listener.count, 0) - deleteMockJson(t) -} -func TestRemoveListener(t *testing.T) { - listener := &apolloDataListener{} - apollo := initMockApollo(t) - mockConfigRes = `{ - "appId": "testApplication_yang", - "cluster": "default", - "namespaceName": "mockDubbog.properties", - "configurations": { - "registries.hangzhouzk.username": "11111" - }, - "releaseKey": "20191104105242-0f13805d89f834a4" -}` - apollo.AddListener(mockNamespace, listener) + //test remove apollo.RemoveListener(mockNamespace, listener) - assert.Equal(t, "", listener.event) listenerCount := 0 apollo.listeners.Range(func(_, value interface{}) bool { apolloListener := value.(*apolloListener) @@ -247,7 +233,6 @@ func TestRemoveListener(t *testing.T) { return true }) assert.Equal(t, listenerCount, 0) - assert.Equal(t, listener.count, 0) deleteMockJson(t) } diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go index 48a35093d20c445f6b6ff391f6a2e0e5b737e8f2..ace5ed026875d2478e5d81b1974a6c60f856378c 100644 --- a/config_center/apollo/listener.go +++ b/config_center/apollo/listener.go @@ -18,34 +18,48 @@ package apollo import ( - "github.com/zouyx/agollo" + "github.com/zouyx/agollo/v3" + "github.com/zouyx/agollo/v3/storage" + "gopkg.in/yaml.v2" ) import ( + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/remoting" ) type apolloListener struct { listeners map[config_center.ConfigurationListener]struct{} } -// NewApolloListener creates a new apolloListener -func NewApolloListener() *apolloListener { +// nolint +func newApolloListener() *apolloListener { return &apolloListener{ listeners: make(map[config_center.ConfigurationListener]struct{}, 0), } } // OnChange process each listener -func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) { - for key, change := range changeEvent.Changes { - for listener := range a.listeners { - listener.Process(&config_center.ConfigChangeEvent{ - ConfigType: getChangeType(change.ChangeType), - Key: key, - Value: change.NewValue, - }) - } +func (a *apolloListener) OnChange(changeEvent *storage.ChangeEvent) { + +} + +// OnNewestChange process each listener by all changes +func (a *apolloListener) OnNewestChange(changeEvent *storage.FullChangeEvent) { + b, err := yaml.Marshal(changeEvent.Changes) + if err != nil { + logger.Errorf("apollo onNewestChange err %+v", + err) + return + } + content := string(b) + for listener := range a.listeners { + listener.Process(&config_center.ConfigChangeEvent{ + ConfigType: remoting.EventTypeUpdate, + Key: changeEvent.Namespace, + Value: content, + }) } } diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go index 9a09b713fad11afdc06310a2d0072454342ccb0b..acfe2609ceb0fc62650b7b494b25084ea4df6946 100644 --- a/config_center/nacos/client.go +++ b/config_center/nacos/client.go @@ -98,6 +98,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error { } nacosAddresses := strings.Split(url.Location, ",") if container.NacosClient() == nil { + //in dubbo ,every registry only connect one node ,so this is []string{r.Address} newClient, err := newNacosClient(os.nacosName, nacosAddresses, timeout, url) if err != nil { logger.Errorf("newNacosClient(name{%s}, nacos address{%v}, timeout{%d}) = error{%v}", @@ -115,6 +116,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error { return perrors.WithMessagef(err, "newNacosClient(address:%+v)", url.Location) } container.NacosClient().SetClient(&configClient) + } return perrors.WithMessagef(nil, "newNacosClient(address:%+v)", url.PrimitiveURL) @@ -167,8 +169,9 @@ func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url commo "serverConfigs": svrConfList, "clientConfig": nacosconst.ClientConfig{ TimeoutMs: uint64(int32(timeout / time.Millisecond)), + ListenInterval: uint64(int32(timeout / time.Millisecond)), NotLoadCacheAtStart: true, - LogDir: url.GetParam(constant.NACOS_LOG_DIR_KEY, logDir), + LogDir: url.GetParam(constant.NACOS_LOG_DIR_KEY, ""), CacheDir: url.GetParam(constant.NACOS_CACHE_DIR_KEY, ""), Endpoint: url.GetParam(constant.NACOS_ENDPOINT, ""), Username: url.GetParam(constant.NACOS_USERNAME, ""), diff --git a/config_center/nacos/facade.go b/config_center/nacos/facade.go index 77a79ed091461ea5184cb2531d985c233ccd92e9..d089ed26d2810e5c5076ec4b6342cbe10d00799d 100644 --- a/config_center/nacos/facade.go +++ b/config_center/nacos/facade.go @@ -22,7 +22,7 @@ import ( "time" ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" ) diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index 3118a9d0529bf8762dc7ee864af32b044b74fd49..57df0363a1d7712e1e02a66595030c207b19c170 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -48,6 +48,7 @@ func (n *nacosDynamicConfiguration) addListener(key string, listener config_cent }) if err != nil { logger.Errorf("nacos : listen config fail, error:%v ", err) + return } newListener := make(map[config_center.ConfigurationListener]context.CancelFunc) newListener[listener] = cancel diff --git a/contributing.md b/contributing.md index 9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51..51301511e01c12e388edf574e5c8660ad4a542a1 100644 --- a/contributing.md +++ b/contributing.md @@ -38,4 +38,27 @@ The title format of the pull request `MUST` follow the following rules: ### 3.3 comment >- 1 there should be comment for every export func/var. ->- 2 the comment should begin with function name/var name. \ No newline at end of file +>- 2 the comment should begin with function name/var name. + +### 3.4 import + +We dubbogo import blocks should be splited into 3 blocks. + +```Go +// block 1: the go internal package +import ( + "fmt" +) + +// block 2: the third package +import ( + "github.com/dubbogo/xxx" + + "github.com/RoaringBitmap/roaring" +) + +// block 3: the dubbo-go package +import ( + "github.com/apache/dubbo-go/common" +) +``` \ No newline at end of file diff --git a/doc/pic/arch/dubbo-go-arch.png b/doc/pic/arch/dubbo-go-arch.png index e5f192715216257929cf4a550a1adf3588ab0e0f..0a28cb6a3af6953d2ad10390ddff5b7d82166ecc 100644 Binary files a/doc/pic/arch/dubbo-go-arch.png and b/doc/pic/arch/dubbo-go-arch.png differ diff --git a/doc/pic/arch/dubbo-go-ext.png b/doc/pic/arch/dubbo-go-ext.png index d065ef4d8e28e507b2ee36fd8c6c6928ab7c9b5d..b60ecdf47ef190f097fccaf2d74070f6712ea80d 100644 Binary files a/doc/pic/arch/dubbo-go-ext.png and b/doc/pic/arch/dubbo-go-ext.png differ diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go index e2275149f1229c87ed4ae3f89dc9ae32d9fe6461..d13e02c06f7b2191e4d01c013a72300e1b095616 100644 --- a/filter/filter_impl/hystrix_filter.go +++ b/filter/filter_impl/hystrix_filter.go @@ -53,10 +53,6 @@ var ( providerConfigOnce sync.Once ) -//The filter in the server end of dubbo-go can't get the invoke result for now, -//this filter ONLY works in CLIENT end (consumer side) temporarily -//Only after the callService logic is integrated into the filter chain of server end then the filter can be used, -//which will be done soon func init() { extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer) extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider) @@ -85,7 +81,47 @@ func NewHystrixFilterError(err error, failByHystrix bool) error { } } -// nolint +/** + * HystrixFilter + * You should add hystrix related configuration in provider or consumer config or both, according to which side you are to apply HystrixFilter. + * For example: + * filter_conf: + * hystrix: + * configs: + * # =========== Define config here ============ + * "Default": + * timeout : 1000 + * max_concurrent_requests : 25 + * sleep_window : 5000 + * error_percent_threshold : 50 + * request_volume_threshold: 20 + * "userp": + * timeout: 2000 + * max_concurrent_requests: 512 + * sleep_window: 4000 + * error_percent_threshold: 35 + * request_volume_threshold: 6 + * "userp_m": + * timeout : 1200 + * max_concurrent_requests : 512 + * sleep_window : 6000 + * error_percent_threshold : 60 + * request_volume_threshold: 16 + * # =========== Define error whitelist which will be ignored by Hystrix counter ============ + * error_whitelist: [".*exception.*"] + * + * # =========== Apply default config here =========== + * default: "Default" + * + * services: + * "com.ikurento.user.UserProvider": + * # =========== Apply service level config =========== + * service_config: "userp" + * # =========== Apply method level config =========== + * methods: + * "GetUser": "userp_m" + * "GetUser1": "userp_m" + */ type HystrixFilter struct { COrP bool //true for consumer res map[string][]*regexp.Regexp @@ -213,11 +249,11 @@ func getConfig(service string, method string, cOrP bool) CommandConfigWithError func initHystrixConfigConsumer() error { if config.GetConsumerConfig().FilterConf == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_consumer") } filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] if filterConfig == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_consumer") } hystrixConfByte, err := yaml.Marshal(filterConfig) if err != nil { @@ -232,11 +268,11 @@ func initHystrixConfigConsumer() error { func initHystrixConfigProvider() error { if config.GetProviderConfig().FilterConf == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_provider") } - filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] + filterConfig := config.GetProviderConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] if filterConfig == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_provider") } hystrixConfByte, err := yaml.Marshal(filterConfig) if err != nil { diff --git a/go.mod b/go.mod index 511f823088b27774ee6c41772d00af7e13a2ea91..c9737b9af90178cf4e689771e962fc7590d875a2 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,18 @@ module github.com/apache/dubbo-go require ( - cloud.google.com/go v0.39.0 // indirect github.com/Microsoft/go-winio v0.4.13 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect + github.com/RoaringBitmap/roaring v0.4.23 github.com/Workiva/go-datastructures v1.0.50 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 - github.com/apache/dubbo-go-hessian2 v1.6.1 + github.com/apache/dubbo-getty v1.3.10 + github.com/apache/dubbo-go-hessian2 v1.6.2 github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible - github.com/coreos/go-semver v0.3.0 // indirect github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/creasty/defaults v1.3.0 github.com/docker/go-connections v0.4.0 // indirect - github.com/dubbogo/getty v1.3.9 github.com/dubbogo/go-zookeeper v1.0.1 github.com/dubbogo/gost v1.9.1 github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect @@ -48,20 +47,19 @@ require ( github.com/prometheus/client_golang v1.1.0 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/shirou/gopsutil v2.19.9+incompatible // indirect - github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.5.1 - github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 + github.com/zouyx/agollo/v3 v3.4.4 go.etcd.io/bbolt v1.3.4 // indirect go.uber.org/atomic v1.6.0 go.uber.org/zap v1.15.0 - google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect google.golang.org/grpc v1.23.0 gopkg.in/yaml.v2 v2.2.8 k8s.io/api v0.16.9 k8s.io/apimachinery v0.16.9 k8s.io/client-go v0.16.9 k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect + ) go 1.13 diff --git a/go.sum b/go.sum index fd448bf3bcdfa8478bd60bbb6b44c1250e79eb7e..c5ab85db14cc95ca332d6f81b0aca9c6a9823090 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,17 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.39.0 h1:UgQP9na6OTfp4dsAiz/eFpFA1C6tPdH5wiRdi19tuMw= -cloud.google.com/go v0.39.0/go.mod h1:rVLT6fkc8chs9sfPtFc1SBH6em7n+ZoXaG+87tDISts= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v40.3.0+incompatible h1:NthZg3psrLxvQLN6rVm07pZ9mv2wvGNaBNGQ3fnPvLE= github.com/Azure/azure-sdk-for-go v40.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= @@ -35,6 +44,7 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -48,6 +58,8 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo= +github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= @@ -60,8 +72,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= -github.com/apache/dubbo-go-hessian2 v1.6.1 h1:mFKeCZzaCkk4mMOyP+LQ85GHbRyqKT7858KS21JQYA4= -github.com/apache/dubbo-go-hessian2 v1.6.1/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/dubbo-getty v1.3.10 h1:ys5mwjPdxG/KwkPjS6EI0RzQtU6p6FCPoKpaFEzpAL0= +github.com/apache/dubbo-getty v1.3.10/go.mod h1:x6rraK01BL5C7jUM2fPl5KMkAxLVIx54ZB8/XEOik9Y= +github.com/apache/dubbo-go-hessian2 v1.6.2 h1:i7F5GjVaUatLQz1x9vUmmSIFj49L8J6rVICdF6xw4qw= +github.com/apache/dubbo-go-hessian2 v1.6.2/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -80,6 +94,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= @@ -133,8 +148,6 @@ github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/dubbogo/getty v1.3.9 h1:Ip/4Yl7GyDzt3ddZhjXN1Vmxvo9y/hdtoJ4yFc7czLk= -github.com/dubbogo/getty v1.3.9/go.mod h1:JIN5lNZznGnBuoYjlEe/yjNmq7eSjKaawHdnBAdtiEo= github.com/dubbogo/go-zookeeper v1.0.1 h1:irLzvOsDOTNsN8Sv9tvYYxVu6DCQfLtziZQtUHmZgz8= github.com/dubbogo/go-zookeeper v1.0.1/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk= @@ -170,12 +183,17 @@ github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2H github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-co-op/gocron v0.1.1 h1:OfDmkqkCguFtFMsm6Eaayci3DADLa8pXvdmOlPU/JcU= github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= @@ -234,11 +252,14 @@ github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -246,6 +267,8 @@ github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/ github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= @@ -262,8 +285,10 @@ github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/hashicorp/consul v1.8.0 h1:yRKMKZyPLqUxl37t4nFt5OuGmTXoFhTJrakhfnYKCYA= github.com/hashicorp/consul v1.8.0/go.mod h1:Gg9/UgAQ9rdY3CTvzQZ6g2jcIb7NlIfjI+0pvLk5D1A= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.5.0 h1:Yo2bneoGy68A7aNwmuETFnPhjyBEm7n3vzRacEVMjvI= github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/consul/sdk v0.5.0 h1:WC4594Wp/LkEeML/OdQKEC1yqBmEYkRp6i7X5u0zDAs= github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -303,6 +328,7 @@ github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2 h1:bHM2aVXwBtBJWxHtkSrWuI4umABCUczs52eiUS9nSiw= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -319,6 +345,7 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+dIzX/E= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= @@ -328,8 +355,10 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5 h1:uk280DXEbQiCOZgCOI3elFSeNxf8YIZiNsbr2pQLYD0= github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.1 h1:XFSOubp8KWB+Jd2PDyaX5xUd5bhSP/+pTDZVDMzZJM8= github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= github.com/hashicorp/memberlist v0.2.2 h1:5+RffWKwqJ71YPu9mWsF7ZOscZmwfasdA8kbdC7AO2g= github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= @@ -341,6 +370,7 @@ github.com/hashicorp/raft v1.1.2 h1:oxEL5DDeurYxLd3UbcY/hccgSPhLLpiBZ1YxtWEq59c= github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea h1:xykPFhrBAS2J0VBzVa5e80b5ZtYuNQtgXjN40qBZlD4= github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0= github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= @@ -433,6 +463,7 @@ github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= @@ -440,9 +471,11 @@ github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eI github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU= @@ -459,6 +492,8 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= @@ -487,8 +522,11 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw= github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -558,8 +596,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 h1:N8Bg45zpk/UcpNGnfJt2y/3lRWASHNTUET8owPYCgYI= -github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQvfjNUeRqaY/uT0tFuvuFY0ulgnczuR684Xic= github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= @@ -567,9 +605,12 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -577,6 +618,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= @@ -587,6 +630,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= @@ -594,6 +639,8 @@ github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYo github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= +github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk= @@ -604,11 +651,13 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= +github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 h1:k8TV7Gz7cpWpOw/dz71fx8cCZdWoPuckHJ/wkJl+meg= -github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY= +github.com/zouyx/agollo/v3 v3.4.4 h1:5G7QNw3fw74Ns8SfnHNhjndV2mlz5Fg8bB7q84ydFYI= +github.com/zouyx/agollo/v3 v3.4.4/go.mod h1:ag0XmE1r4iAgPd6PUnU9TJ0DMEjM1VKX1HUNqQJ2ywU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -632,20 +681,31 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -661,6 +721,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -692,6 +753,7 @@ golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -700,6 +762,8 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -727,41 +791,58 @@ golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw= google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190508193815-b515fa19cec8/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -780,6 +861,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -790,6 +873,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -815,6 +899,7 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLy k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/registry/directory/directory.go b/registry/directory/directory.go index 2fbf9410f76c473362964c9ef148e3c581d3d045..8940d2ab5e6d71116149a8bb2bf617776a73ed93 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -18,10 +18,14 @@ package directory import ( + "fmt" + "net/url" + "os" "sync" ) import ( + gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "go.uber.org/atomic" ) @@ -29,6 +33,7 @@ import ( import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/cluster/directory" + "github.com/apache/dubbo-go/cluster/router/chain" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -55,6 +60,7 @@ type RegistryDirectory struct { serviceType string registry registry.Registry cacheInvokersMap *sync.Map // use sync.map + consumerURL *common.URL cacheOriginUrl *common.URL configurators []config_center.Configurator consumerConfigurationListener *consumerConfigurationListener @@ -75,6 +81,15 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster. serviceType: url.SubURL.Service(), registry: registry, } + + dir.consumerURL = dir.getConsumerUrl(url.SubURL) + + if routerChain, err := chain.NewRouterChain(dir.consumerURL); err == nil { + dir.BaseDirectory.SetRouterChain(routerChain) + } else { + logger.Warnf("fail to create router chain with url: %s, err is: %v", url.SubURL, err) + } + dir.consumerConfigurationListener = newConsumerConfigurationListener(dir) go dir.subscribe(url.SubURL) @@ -145,6 +160,9 @@ func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) { newInvokers := dir.toGroupInvokers() dir.listenerLock.Lock() dir.cacheInvokers = newInvokers + if res != nil { + dir.RouterChain().SetInvokers(newInvokers) + } dir.listenerLock.Unlock() // After dir.cacheInvokers is updated,destroy the oldInvoker // Ensure that no request will enter the oldInvoker @@ -251,7 +269,7 @@ func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.In if routerChain == nil { return invokers } - return routerChain.Route(invokers, dir.cacheOriginUrl, invocation) + return routerChain.Route(dir.consumerURL, invocation) } // IsAvailable whether the directory is available @@ -287,6 +305,24 @@ func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) { doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl) } +func (dir *RegistryDirectory) getConsumerUrl(c *common.URL) *common.URL { + processID := fmt.Sprintf("%d", os.Getpid()) + localIP, _ := gxnet.GetLocalIP() + + params := url.Values{} + c.RangeParams(func(key, value string) bool { + params.Add(key, value) + return true + }) + + params.Add("pid", processID) + params.Add("ip", localIP) + params.Add("protocol", c.Protocol) + + return common.NewURLWithOptions(common.WithProtocol("consumer"), common.WithIp(localIP), common.WithPath(c.Path), + common.WithParams(params)) +} + func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common.URL) { for _, v := range configurators { v.Configure(targetUrl) diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index f27e7ce8ba5ab3515f8290b208b4823872ba48a3..cc4ea257ccabb5591689ec961ae279e12f24dd29 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -24,8 +24,8 @@ import ( ) import ( + "github.com/apache/dubbo-getty" "github.com/coreos/etcd/embed" - "github.com/dubbogo/getty" "github.com/stretchr/testify/suite" ) diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go index 7c5162670d85f661fb8460cc69537ac9b7a12a23..88895855689df42f2cd66ec158e6b8f2806ef6f0 100644 --- a/registry/kubernetes/registry.go +++ b/registry/kubernetes/registry.go @@ -26,7 +26,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" v1 "k8s.io/api/core/v1" diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go index 669900736214178799294f1ba1f97841bb6b81a4..cf6a73d38fbefc1ecb6a80e4911bb0d8fb13a6f6 100644 --- a/registry/nacos/listener.go +++ b/registry/nacos/listener.go @@ -132,7 +132,7 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error) instance := generateInstance(services[i]) newInstanceMap[host] = instance if old, ok := nl.instanceMap[host]; !ok { - // instance is not exist in cache, add it to cache + // instance does not exist in cache, add it to cache addInstances = append(addInstances, instance) } else { // instance is not different from cache, update it to cache @@ -144,7 +144,7 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error) for host, inst := range nl.instanceMap { if _, ok := newInstanceMap[host]; !ok { - // cache instance is not exist in new instance list, remove it from cache + // cache instance does not exist in new instance list, remove it from cache delInstances = append(delInstances, inst) } } @@ -188,7 +188,8 @@ func (nl *nacosListener) startListen() error { } serviceName := getSubscribeName(nl.listenUrl) nl.subscribeParam = &vo.SubscribeParam{ServiceName: serviceName, SubscribeCallback: nl.Callback} - return nl.namingClient.Subscribe(nl.subscribeParam) + go nl.namingClient.Subscribe(nl.subscribeParam) + return nil } func (nl *nacosListener) stopListen() error { diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index 4c669b2cee74b95ceb3bc8287f145ccd6b99bc0b..c0608ad7989e69c104f07aa95e9a77fb9ac212fa 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -117,6 +117,18 @@ func (proto *registryProtocol) initConfigurationListeners() { proto.providerConfigurationListener = newProviderConfigurationListener(proto.overrideListeners) } +// nolint +func (proto *registryProtocol) GetRegistries() []registry.Registry { + var rs []registry.Registry + proto.registries.Range(func(_, v interface{}) bool { + if r, ok := v.(registry.Registry); ok { + rs = append(rs, r) + } + return true + }) + return rs +} + // Refer provider service from registry center func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { var registryUrl = url @@ -178,6 +190,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { reg = getRegistry(registryUrl) proto.registries.Store(registryUrl.Key(), reg) + logger.Infof("Export proto:%p registries address:%p", proto, proto.registries) } else { reg = regI.(registry.Registry) } @@ -334,14 +347,12 @@ func (proto *registryProtocol) Destroy() { ivk.Destroy() } proto.invokers = []protocol.Invoker{} - proto.bounds.Range(func(key, value interface{}) bool { exporter := value.(protocol.Exporter) exporter.Unexport() proto.bounds.Delete(key) return true }) - proto.registries.Range(func(key, value interface{}) bool { reg := value.(registry.Registry) if reg.IsAvailable() { @@ -373,7 +384,7 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) { regURL.SubURL = providerURL } -// GetProtocol return the singleton RegistryProtocol +// GetProtocol return the singleton registryProtocol func GetProtocol() protocol.Protocol { once.Do(func() { regProtocol = newRegistryProtocol() diff --git a/cluster/router/chan.go b/registry/registry_factory.go similarity index 86% rename from cluster/router/chan.go rename to registry/registry_factory.go index 6904e1734a7cbdaa00afa1b30797d19ca502453c..caefbce2eb833437a0e55323fad5ab334210db4e 100644 --- a/cluster/router/chan.go +++ b/registry/registry_factory.go @@ -15,11 +15,10 @@ * limitations under the License. */ -package router +package registry -// Chain -type Chain interface { - router - // AddRouters Add routers - AddRouters([]PriorityRouter) +// RegistryFactory +type RegistryFactory interface { + // GetRegistries get registries + GetRegistries() []Registry } diff --git a/registry/service_discovery_holder.go b/registry/service_discovery_holder.go new file mode 100644 index 0000000000000000000000000000000000000000..3bcf72612a7bb5ab2460b4063a2132716dd6249e --- /dev/null +++ b/registry/service_discovery_holder.go @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package registry + +// ServiceDiscoveryHolder we can get a service discovery +// it always be a service discovery registry +type ServiceDiscoveryHolder interface { + // GetServiceDiscovery get service discovery + GetServiceDiscovery() ServiceDiscovery +} diff --git a/registry/servicediscovery/service_discovery_registry.go b/registry/servicediscovery/service_discovery_registry.go index 061d832b0328a5e1754c7804bf40cf83ac216a8b..7576804eb563e16a043f63f17db2532f48c878f1 100644 --- a/registry/servicediscovery/service_discovery_registry.go +++ b/registry/servicediscovery/service_discovery_registry.go @@ -28,7 +28,6 @@ import ( import ( cm "github.com/Workiva/go-datastructures/common" gxset "github.com/dubbogo/gost/container/set" - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "go.uber.org/atomic" ) @@ -176,18 +175,6 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error { logger.Warnf("The URL[%s] has been registry!", url.String()) } - // we try to register this instance. Dubbo do this in org.apache.dubbo.config.bootstrap.DubboBootstrap - // But we don't want to design a similar bootstrap class. - ins, err := createInstance(url) - if err != nil { - return perrors.WithMessage(err, "could not create servcie instance, please check your service url") - } - - err = s.serviceDiscovery.Register(ins) - if err != nil { - return perrors.WithMessage(err, "register the service failed") - } - err = s.metaDataService.PublishServiceDefinition(url) if err != nil { return perrors.WithMessage(err, "publish the service definition failed. ") @@ -198,36 +185,6 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error { url.Protocol) } -func createInstance(url common.URL) (registry.ServiceInstance, error) { - appConfig := config.GetApplicationConfig() - port, err := strconv.ParseInt(url.Port, 10, 32) - if err != nil { - return nil, perrors.WithMessage(err, "invalid port: "+url.Port) - } - - host := url.Ip - if len(host) == 0 { - host, err = gxnet.GetLocalIP() - if err != nil { - return nil, perrors.WithMessage(err, "could not get the local Ip") - } - } - - // usually we will add more metadata - metadata := make(map[string]string, 8) - metadata[constant.METADATA_STORAGE_TYPE_PROPERTY_NAME] = appConfig.MetadataType - - return ®istry.DefaultServiceInstance{ - ServiceName: appConfig.Name, - Host: host, - Port: int(port), - Id: host + constant.KEY_SEPARATOR + url.Port, - Enable: true, - Healthy: true, - Metadata: metadata, - }, nil -} - func shouldRegister(url common.URL) bool { side := url.GetParam(constant.SIDE_KEY, "") if side == constant.PROVIDER_PROTOCOL { @@ -675,7 +632,7 @@ func (icn *InstanceChangeNotify) Notify(event observer.Event) { if se, ok := event.(*registry.ServiceInstancesChangedEvent); ok { sdr := icn.serviceDiscoveryRegistry - sdr.subscribe(sdr.url, icn.notify, se.ServiceName, se.Instances) + sdr.subscribe(sdr.url.SubURL, icn.notify, se.ServiceName, se.Instances) } } diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go index 2edbb6650890d6d655d8356e1c0a2979a022f0a8..52b1cce3e4618e9c2669e7a3b37256ebe6d61c41 100644 --- a/remoting/etcdv3/facade.go +++ b/remoting/etcdv3/facade.go @@ -23,7 +23,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" ) diff --git a/remoting/exchange.go b/remoting/exchange.go index b97d6073c1f734d91c094372cf7de0919d088d99..848d9cbbcc23b0f565c45b646cf443be3f811efc 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -77,7 +77,7 @@ type Response struct { Result interface{} } -// NewResponse aims to create Response +// NewResponse create to a new Response. func NewResponse(id int64, version string) *Response { return &Response{ ID: id, diff --git a/remoting/getty/config.go b/remoting/getty/config.go index 4eaa4d428ec687a5fbc53b2b87e1b73b9e08b0cc..4213a8705d883e030aaed15d80e222e6b7bf5c47 100644 --- a/remoting/getty/config.go +++ b/remoting/getty/config.go @@ -22,10 +22,13 @@ import ( ) import ( - "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) +import ( + "github.com/apache/dubbo-go/config" +) + type ( // GettySessionParam ... GettySessionParam struct { @@ -182,9 +185,9 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) } - if c.heartbeatPeriod >= time.Duration(getty.MaxWheelTimeSpan) { + if c.heartbeatPeriod >= time.Duration(config.MaxWheelTimeSpan) { return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s", - c.HeartbeatPeriod, time.Duration(getty.MaxWheelTimeSpan)) + c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan)) } if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { @@ -202,9 +205,9 @@ func (c *ServerConfig) CheckValidity() error { return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) } - if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { + if c.sessionTimeout >= time.Duration(config.MaxWheelTimeSpan) { return perrors.WithMessagef(err, "session_timeout %s should be less than %s", - c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) + c.SessionTimeout, time.Duration(config.MaxWheelTimeSpan)) } return perrors.WithStack(c.GettySessionParam.CheckValidity()) diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 63ca165bec224a674d57c761224e07d03a949a6d..6af3971f5c1f9ff5b0fafbc00ae2ba3f44eb34b5 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -18,13 +18,12 @@ package getty import ( - "github.com/apache/dubbo-go/common/constant" "math/rand" "time" ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" gxsync "github.com/dubbogo/gost/sync" perrors "github.com/pkg/errors" "gopkg.in/yaml.v2" @@ -32,6 +31,7 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/remoting" diff --git a/remoting/getty/getty_server.go b/remoting/getty/getty_server.go index 693b42fbbb0dbc280749a999fa2702515f7f5400..731ad8795236fa529a786e1bff2faf5735df2df6 100644 --- a/remoting/getty/getty_server.go +++ b/remoting/getty/getty_server.go @@ -24,7 +24,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" gxsync "github.com/dubbogo/gost/sync" "gopkg.in/yaml.v2" ) diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 777e14c2ecb344d3fa2aa2084b79c7805f542e28..198ec51daa6fb8dd8d1d55d484e179f685f94275 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -25,8 +25,8 @@ import ( ) import ( + "github.com/apache/dubbo-getty" hessian "github.com/apache/dubbo-go-hessian2" - "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) diff --git a/remoting/getty/pool.go b/remoting/getty/pool.go index dc439e1ba493b3b2fe4e77f0d3f4d90a0ec4bd6b..464cff956e0b4667944942be1c4721a05845bd33 100644 --- a/remoting/getty/pool.go +++ b/remoting/getty/pool.go @@ -28,7 +28,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" ) diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index e290d439b23f38ce528aa5f7e960a985df7c6acd..ae96f35391a9c23bad68c78fbb89f4d1667ef709 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -22,8 +22,8 @@ import ( ) import ( + "github.com/apache/dubbo-getty" hessian "github.com/apache/dubbo-go-hessian2" - "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index d5d9e6e74858e3ec520aedee5b8ba059baf928d8..2a034390c016864f95303b12b6c56533771075f3 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -21,7 +21,7 @@ import ( "sync" ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" ) diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index 4f50c18ab61ba6faf373dfd0f831c14ae7ab6d5d..486a67e20a0736be20813226b622f1bfa5bd87c0 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -25,7 +25,7 @@ import ( ) import ( - "github.com/dubbogo/getty" + "github.com/apache/dubbo-getty" "github.com/dubbogo/go-zookeeper/zk" perrors "github.com/pkg/errors" )