You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by GitBox <gi...@apache.org> on 2018/07/07 15:36:36 UTC
[GitHub] asifdxtreme closed pull request #387: SCB-680 Optimize find
instance api
asifdxtreme closed pull request #387: SCB-680 Optimize find instance api
URL: https://github.com/apache/incubator-servicecomb-service-center/pull/387
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/pkg/async/service.go b/pkg/async/service.go
index b83b8eaa..3551e560 100644
--- a/pkg/async/service.go
+++ b/pkg/async/service.go
@@ -26,10 +26,10 @@ import (
)
const (
- maxExecutorCount = 1000
+ initExecutorCount = 1000
removeExecutorInterval = 30 * time.Second
initExecutorTTL = 4
- executeInterval = 100 * time.Millisecond
+ executeInterval = 1 * time.Second
compactTimes = 2
)
@@ -107,12 +107,11 @@ func (lat *TaskService) daemon(ctx context.Context) {
util.SafeCloseChan(lat.ready)
ticker := time.NewTicker(removeExecutorInterval)
max := 0
+ timer := time.NewTimer(executeInterval)
+ defer timer.Stop()
for {
- timer := time.NewTimer(executeInterval)
select {
case <-ctx.Done():
- timer.Stop()
-
util.Logger().Debugf("daemon thread exited for TaskService is stopped")
return
case <-timer.C:
@@ -127,8 +126,10 @@ func (lat *TaskService) daemon(ctx context.Context) {
for _, s := range slice {
s.Execute() // non-blocked
}
+
+ timer.Reset(executeInterval)
case <-ticker.C:
- timer.Stop()
+ util.ResetTimer(timer, executeInterval)
lat.lock.RLock()
l := len(lat.executors)
@@ -154,7 +155,7 @@ func (lat *TaskService) daemon(ctx context.Context) {
}
l = len(lat.executors)
- if max > maxExecutorCount && max > l*compactTimes {
+ if max > initExecutorCount && max > l*compactTimes {
lat.renew()
max = l
}
@@ -200,7 +201,7 @@ func (lat *TaskService) Ready() <-chan struct{} {
}
func (lat *TaskService) renew() {
- newExecutor := make(map[string]*executorWithTTL, maxExecutorCount)
+ newExecutor := make(map[string]*executorWithTTL)
for k, e := range lat.executors {
newExecutor[k] = e
}
diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go
new file mode 100644
index 00000000..7f53552b
--- /dev/null
+++ b/pkg/cache/cache.go
@@ -0,0 +1,46 @@
+/*
+ * 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 cache
+
+import (
+ "golang.org/x/net/context"
+ "sync"
+)
+
+type Cache struct {
+ data context.Context
+ lock sync.RWMutex
+}
+
+func (c *Cache) Set(k string, v interface{}) {
+ c.lock.Lock()
+ c.data = context.WithValue(c.data, k, v)
+ c.lock.Unlock()
+}
+
+func (c *Cache) Get(k string) (v interface{}) {
+ c.lock.RLock()
+ v = c.data.Value(k)
+ c.lock.RUnlock()
+ return
+}
+
+func NewCache() *Cache {
+ return &Cache{
+ data: context.Background(),
+ }
+}
diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go
new file mode 100644
index 00000000..331666ea
--- /dev/null
+++ b/pkg/cache/cache_test.go
@@ -0,0 +1,178 @@
+/*
+ * 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 cache
+
+import (
+ "fmt"
+ "golang.org/x/net/context"
+ "testing"
+ "time"
+)
+
+type level1 struct {
+}
+
+func (l *level1) Name(ctx context.Context) string {
+ return ctx.Value("key1").(string)
+}
+
+func (l *level1) Init(ctx context.Context, parent *Node) (node *Node, err error) {
+ p := l.Name(ctx)
+ if p == "err" {
+ return nil, fmt.Errorf("wrong logic")
+ }
+
+ if p == "null" {
+ return
+ }
+
+ node = NewNode()
+ node.Cache.Set("a", "a")
+ node.Cache.Set("b", "b")
+ node.Cache.Set("c", "c")
+ return
+}
+
+type level2 struct {
+ changed string
+}
+
+func (l *level2) Name(ctx context.Context) string {
+ return ctx.Value("key2").(string)
+}
+
+func (l *level2) Init(ctx context.Context, parent *Node) (node *Node, err error) {
+ if parent == nil {
+ return
+ }
+
+ p := l.Name(ctx)
+ if p == "err" {
+ return nil, fmt.Errorf("wrong logic")
+ }
+
+ if p == "null" {
+ return
+ }
+
+ node = NewNode()
+ if len(l.changed) == 0 {
+ i := parent.Cache.Get(p)
+ node.Cache.Set("aa", i)
+ } else {
+ node.Cache.Set("aa", l.changed)
+ }
+
+ return
+}
+
+func TestTree_Get(t *testing.T) {
+ tree := NewTree(Configure().WithMaxSize(1).WithTTL(time.Second))
+ node, err := tree.Get(context.Background())
+ if node != nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ tree.Remove(context.Background())
+
+ l2 := &level2{}
+ tree.AddFilter(&level1{}, l2)
+
+ ctx := context.WithValue(context.WithValue(context.Background(), "key1", "err"), "key2", "")
+ node, err = tree.Get(ctx)
+ if node != nil || err == nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "null"), "key2", ""))
+ if node != nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "err"))
+ node, err = tree.Get(ctx)
+ if node != nil || err == nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "null"))
+ if node != nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "err"))
+ if node != nil || err == nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("aa") != "b" {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "a"))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("aa") != "a" {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("aa") != "b" {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ tree.Remove(context.WithValue(ctx, "key1", "1"))
+
+ l2.changed = "changed"
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("aa") != "changed" {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ l2.changed = ""
+ node, err = tree.Get(context.WithValue(context.WithValue(ctx, "key1", "1"), "key2", "b"),
+ Options().Temporary(true))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("aa") != "b" {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ node, err = tree.Get(context.WithValue(ctx, "key1", "1"), Options().ToLevel(1))
+ if node == nil || err != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+ if node.Cache.Get("a") != "a" || node.Cache.Get("aa") != nil {
+ t.Fatalf("TestTree_Get failed")
+ }
+
+ childs := node.ChildNodes()
+ if len(childs) != 1 || childs[0].Cache.Get("aa") != "changed" {
+ t.Fatalf("TestTree_Get failed")
+ }
+}
diff --git a/pkg/cache/config.go b/pkg/cache/config.go
new file mode 100644
index 00000000..fac805d6
--- /dev/null
+++ b/pkg/cache/config.go
@@ -0,0 +1,49 @@
+/*
+ * 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 cache
+
+import "time"
+
+type Config struct {
+ ttl time.Duration
+ max int64
+}
+
+func (c *Config) TTL() time.Duration {
+ return c.ttl
+}
+
+func (c *Config) WithTTL(ttl time.Duration) *Config {
+ c.ttl = ttl
+ return c
+}
+
+func (c *Config) MaxSize() int64 {
+ return c.max
+}
+
+func (c *Config) WithMaxSize(s int64) *Config {
+ c.max = s
+ return c
+}
+
+func Configure() *Config {
+ return &Config{
+ ttl: 5 * time.Minute,
+ max: 5000,
+ }
+}
diff --git a/pkg/cache/filter.go b/pkg/cache/filter.go
new file mode 100644
index 00000000..51c64481
--- /dev/null
+++ b/pkg/cache/filter.go
@@ -0,0 +1,24 @@
+/*
+ * 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 cache
+
+import "golang.org/x/net/context"
+
+type Filter interface {
+ Name(ctx context.Context) string
+ Init(ctx context.Context, parent *Node) (*Node, error)
+}
diff --git a/pkg/cache/node.go b/pkg/cache/node.go
new file mode 100644
index 00000000..0e9b4393
--- /dev/null
+++ b/pkg/cache/node.go
@@ -0,0 +1,44 @@
+/*
+ * 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 cache
+
+import "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+
+type Node struct {
+ // user data
+ Cache *Cache
+ // tree will set the value below after the node added in.
+ Name string
+ Tree *Tree
+ Childs *util.ConcurrentMap
+ Level int
+}
+
+func (n *Node) ChildNodes() (nodes []*Node) {
+ n.Childs.ForEach(func(item util.MapItem) (next bool) {
+ nodes = append(nodes, item.Value.(*Node))
+ return true
+ })
+ return
+}
+
+func NewNode() *Node {
+ return &Node{
+ Cache: NewCache(),
+ Childs: util.NewConcurrentMap(0),
+ }
+}
diff --git a/pkg/cache/option.go b/pkg/cache/option.go
new file mode 100644
index 00000000..4250ca18
--- /dev/null
+++ b/pkg/cache/option.go
@@ -0,0 +1,38 @@
+/*
+ * 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 cache
+
+type Option struct {
+ Level int
+ NoCache bool
+}
+
+// get node before level l
+func (op Option) ToLevel(l int) Option {
+ op.Level = l
+ return op
+}
+
+// get node without cache
+func (op Option) Temporary(b bool) Option {
+ op.NoCache = b
+ return op
+}
+
+func Options() (op Option) {
+ return
+}
diff --git a/pkg/cache/option_test.go b/pkg/cache/option_test.go
new file mode 100644
index 00000000..f756a54d
--- /dev/null
+++ b/pkg/cache/option_test.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 cache
+
+import "testing"
+
+func BenchmarkWithLevel(b *testing.B) {
+ b.RunParallel(func(pb *testing.PB) {
+ for pb.Next() {
+ op := Options().ToLevel(3).ToLevel(2).ToLevel(1)
+ if op.Level != 1 {
+ b.Fatalf("BenchmarkWithLevel failed")
+ }
+ }
+ })
+ b.ReportAllocs()
+ // 2000000000 0.28 ns/op 0 B/op 0 allocs/op
+}
diff --git a/pkg/cache/tree.go b/pkg/cache/tree.go
new file mode 100644
index 00000000..9c89df08
--- /dev/null
+++ b/pkg/cache/tree.go
@@ -0,0 +1,160 @@
+/*
+ * 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 cache
+
+import (
+ "errors"
+ "github.com/karlseguin/ccache"
+ "golang.org/x/net/context"
+ "sync"
+)
+
+var errNilNode = errors.New("nil node")
+
+type Tree struct {
+ Config *Config
+ roots *ccache.Cache
+ filters []Filter
+ lock sync.RWMutex
+}
+
+func (t *Tree) AddFilter(fs ...Filter) *Tree {
+ t.lock.Lock()
+ for _, f := range fs {
+ t.filters = append(t.filters, f)
+ }
+ t.lock.Unlock()
+ return t
+}
+
+func (t *Tree) Get(ctx context.Context, ops ...Option) (node *Node, err error) {
+ var op Option
+ if len(ops) > 0 {
+ op = ops[0]
+ }
+
+ var (
+ parent *Node
+ i int
+ )
+
+ if !op.NoCache {
+ if parent, err = t.getOrCreateRoot(ctx); parent == nil {
+ return
+ }
+ i++
+ // parent may be a temp root in concurrent scene
+ }
+
+ for ; i < len(t.filters); i++ {
+ if op.Level > 0 && op.Level == i {
+ break
+ }
+ if parent, err = t.getOrCreateNode(ctx, i, parent); parent == nil {
+ break
+ }
+ }
+ node = parent
+ return
+}
+
+func (t *Tree) Remove(ctx context.Context) {
+ if len(t.filters) == 0 {
+ return
+ }
+
+ t.roots.Delete(t.filters[0].Name(ctx))
+}
+
+func (t *Tree) getOrCreateRoot(ctx context.Context) (node *Node, err error) {
+ if len(t.filters) == 0 {
+ return
+ }
+
+ filter := t.filters[0]
+ name := filter.Name(ctx)
+ item, err := t.roots.Fetch(name, t.Config.TTL(), func() (interface{}, error) {
+ node, err := t.getOrCreateNode(ctx, 0, nil)
+ if err != nil {
+ return nil, err
+ }
+ if node == nil {
+ return nil, errNilNode
+ }
+ return node, nil
+ })
+ switch err {
+ case nil:
+ node = item.Value().(*Node)
+ case errNilNode:
+ err = nil
+ }
+ return
+}
+
+func (t *Tree) getOrCreateNode(ctx context.Context, idx int, parent *Node) (node *Node, err error) {
+ filter := t.filters[idx]
+ name := t.nodeFullName(filter.Name(ctx), parent)
+
+ if parent == nil {
+ // new a temp node
+ return t.createNode(ctx, idx, name, parent)
+ }
+
+ item, err := parent.Childs.Fetch(name, func() (interface{}, error) {
+ node, err := t.createNode(ctx, idx, name, parent)
+ if err != nil {
+ return nil, err
+ }
+ if node == nil {
+ return nil, errNilNode
+ }
+ return node, nil
+ })
+ switch err {
+ case nil:
+ node = item.(*Node)
+ case errNilNode:
+ err = nil
+ }
+ return
+}
+
+func (t *Tree) nodeFullName(name string, parent *Node) string {
+ if parent != nil {
+ name = parent.Name + "." + name
+ }
+ return name
+}
+
+func (t *Tree) createNode(ctx context.Context, idx int, name string, parent *Node) (node *Node, err error) {
+ node, err = t.filters[idx].Init(ctx, parent)
+ if node == nil {
+ return
+ }
+ node.Name = name
+ node.Tree = t
+ node.Level = idx
+ return
+}
+
+func NewTree(cfg *Config) *Tree {
+ return &Tree{
+ Config: cfg,
+ roots: ccache.New(ccache.Configure().MaxSize(cfg.MaxSize())),
+ }
+}
diff --git a/pkg/util/concurrent_map.go b/pkg/util/concurrent_map.go
index 546ec47a..fce2c3ce 100644
--- a/pkg/util/concurrent_map.go
+++ b/pkg/util/concurrent_map.go
@@ -46,18 +46,37 @@ func (cm *ConcurrentMap) Put(key, val interface{}) (old interface{}) {
return
}
-func (cm *ConcurrentMap) PutIfAbsent(key, val interface{}) (old interface{}) {
- var b bool
+func (cm *ConcurrentMap) PutIfAbsent(key, val interface{}) (exist interface{}) {
cm.init()
cm.mux.Lock()
- old, b = cm.items[key]
+ var b bool
+ exist, b = cm.items[key]
if !b {
- cm.items[key] = val
+ cm.items[key], exist = val, val
}
cm.mux.Unlock()
return
}
+func (cm *ConcurrentMap) Fetch(key interface{}, f func() (interface{}, error)) (exist interface{}, err error) {
+ cm.init()
+ cm.mux.RLock()
+ var b bool
+ exist, b = cm.items[key]
+ cm.mux.RUnlock()
+ if !b {
+ cm.mux.Lock()
+ exist, b = cm.items[key]
+ if !b {
+ if exist, err = f(); err == nil {
+ cm.items[key] = exist
+ }
+ }
+ cm.mux.Unlock()
+ }
+ return
+}
+
func (cm *ConcurrentMap) Get(key interface{}) (val interface{}, b bool) {
cm.init()
cm.mux.RLock()
diff --git a/pkg/util/concurrent_map_test.go b/pkg/util/concurrent_map_test.go
index 344de5d8..9259d62a 100644
--- a/pkg/util/concurrent_map_test.go
+++ b/pkg/util/concurrent_map_test.go
@@ -42,14 +42,8 @@ func TestConcurrentMap(t *testing.T) {
if v.(string) != "1" {
t.Fatalf("TestConcurrentMap Put an item again failed.")
}
- v = cm.PutIfAbsent("b", "1")
- if v != nil {
- t.Fatalf("TestConcurrentMap PutIfAbsent a not exist item failed.")
- }
- v = cm.PutIfAbsent("a", "3")
- if v.(string) != "2" {
- t.Fatalf("TestConcurrentMap PutIfAbsent an item failed.")
- }
+ cm.PutIfAbsent("b", "1")
+ cm.PutIfAbsent("a", "3")
v, b = cm.Get("a")
if !b || v.(string) != "2" {
t.Fatalf("TestConcurrentMap Get an item after PutIfAbsent failed.")
diff --git a/pkg/util/goroutines.go b/pkg/util/goroutines.go
index 7ee48a98..74689f3e 100644
--- a/pkg/util/goroutines.go
+++ b/pkg/util/goroutines.go
@@ -63,15 +63,12 @@ func (g *GoRoutine) loop(f func(context.Context)) {
g.wg.Add(1)
defer g.wg.Done()
defer func() { <-g.concurrent }()
+
timer := time.NewTimer(GlobalPoolConfig.IdleTimeout)
+ defer timer.Stop()
for {
g.execute(f)
- if !timer.Stop() {
- <-timer.C
- }
- timer.Reset(GlobalPoolConfig.IdleTimeout)
-
select {
case <-timer.C:
return
@@ -79,6 +76,7 @@ func (g *GoRoutine) loop(f func(context.Context)) {
if f == nil {
return
}
+ ResetTimer(timer, GlobalPoolConfig.IdleTimeout)
}
}
}
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 44b8b09d..5c3b3f06 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -211,3 +211,19 @@ func SliceHave(arr []string, str string) bool {
}
return false
}
+
+// do not call after drain timer.C channel
+func ResetTimer(timer *time.Timer, d time.Duration) {
+ if !timer.Stop() {
+ // timer is expired: can not find the timer in timer stack
+ // select {
+ // case <-timer.C:
+ // // here block when drain channel call before timer.Stop()
+ // default:
+ // // here will cause a BUG When sendTime() after drain channel
+ // // BUG: timer.C still trigger even after timer.Reset()
+ // }
+ <-timer.C
+ }
+ timer.Reset(d)
+}
diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go
index 932aa11d..596e9316 100644
--- a/pkg/util/util_test.go
+++ b/pkg/util/util_test.go
@@ -18,6 +18,7 @@ package util
import (
"testing"
+ "time"
)
func TestInt16ToInt64(t *testing.T) {
@@ -72,3 +73,12 @@ func TestFileLastName(t *testing.T) {
t.Fatal("TestFileLastName 'b' failed", n)
}
}
+
+func TestResetTimer(t *testing.T) {
+ timer := time.NewTimer(time.Microsecond)
+ <-timer.C
+ ResetTimer(timer, time.Microsecond)
+ <-timer.C
+ ResetTimer(timer, time.Second)
+ <-timer.C
+}
diff --git a/server/broker/service.go b/server/broker/service.go
index f4ea5264..c36cb3e1 100644
--- a/server/broker/service.go
+++ b/server/broker/service.go
@@ -151,7 +151,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
versionObjects := make(map[int32]Version)
for i := 0; i < len(versions.Kvs); i++ {
version := &Version{}
- err = json.Unmarshal(versions.Kvs[i].Value, version)
+ err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
if err != nil {
return nil, err
}
@@ -174,7 +174,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
participantToVersionObj := make(map[int32]Version)
for i := 0; i < len(pactVersions.Kvs); i++ {
pactVersion := &PactVersion{}
- err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+ err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
if err != nil {
return nil, err
}
@@ -206,7 +206,7 @@ func (*BrokerService) RetrieveProviderPacts(ctx context.Context,
consumerInfoArr := make([]*ConsumerInfo, 0)
for i := 0; i < len(participants.Kvs); i++ {
participant := &Participant{}
- err = json.Unmarshal(participants.Kvs[i].Value, participant)
+ err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
if err != nil {
return nil, err
}
@@ -304,7 +304,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
versionObjects := make(map[int32]Version)
for i := 0; i < len(versions.Kvs); i++ {
version := &Version{}
- err = json.Unmarshal(versions.Kvs[i].Value, version)
+ err = json.Unmarshal(versions.Kvs[i].Value.([]byte), version)
if err != nil {
return nil, err
}
@@ -327,7 +327,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
participantToVersionObj := make(map[int32]Version)
for i := 0; i < len(pactVersions.Kvs); i++ {
pactVersion := &PactVersion{}
- err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+ err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
if err != nil {
return nil, err
}
@@ -359,7 +359,7 @@ func (*BrokerService) GetAllProviderPacts(ctx context.Context,
consumerInfoArr := make([]*ConsumerInfo, 0)
for i := 0; i < len(participants.Kvs); i++ {
participant := &Participant{}
- err = json.Unmarshal(participants.Kvs[i].Value, participant)
+ err = json.Unmarshal(participants.Kvs[i].Value.([]byte), participant)
if err != nil {
return nil, err
}
@@ -466,7 +466,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
verificationDetailsArr := make([]*VerificationDetail, 0)
for j := 0; j < len(pactVersions.Kvs); j++ {
pactVersion := &PactVersion{}
- err = json.Unmarshal(pactVersions.Kvs[j].Value, &pactVersion)
+ err = json.Unmarshal(pactVersions.Kvs[j].Value.([]byte), &pactVersion)
if err != nil {
PactLogger.Errorf(nil, "verification result retrieve request failed, pact version cannot be searched.")
return &RetrieveVerificationResponse{
@@ -488,7 +488,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
var lastVerificationResult *Verification
for i := 0; i < len(verifications.Kvs); i++ {
verification := &Verification{}
- err = json.Unmarshal(verifications.Kvs[i].Value, &verification)
+ err = json.Unmarshal(verifications.Kvs[i].Value.([]byte), &verification)
if err != nil {
PactLogger.Errorf(nil, "verification result retrieve request failed, verification result unmarshall error.")
return &RetrieveVerificationResponse{
@@ -525,7 +525,7 @@ func (*BrokerService) RetrieveVerificationResults(ctx context.Context, in *Retri
var providerParticipant *Participant
for i := 0; i < len(participants.Kvs); i++ {
participant := &Participant{}
- err = json.Unmarshal(participants.Kvs[i].Value, &participant)
+ err = json.Unmarshal(participants.Kvs[i].Value.([]byte), &participant)
if err != nil {
PactLogger.Errorf(nil, "verification result retrieve request failed, verification result unmarshall error.")
return &RetrieveVerificationResponse{
@@ -635,7 +635,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
pactExists := false
for i := 0; i < len(pacts.Kvs); i++ {
pact := &Pact{}
- err = json.Unmarshal(pacts.Kvs[i].Value, &pact)
+ err = json.Unmarshal(pacts.Kvs[i].Value.([]byte), &pact)
if err != nil {
PactLogger.Errorf(nil, "verification result publish request failed, pact cannot be searched.")
return &PublishVerificationResponse{
@@ -675,7 +675,7 @@ func (*BrokerService) PublishVerificationResults(ctx context.Context, in *Publis
if len(verifications.Kvs) != 0 {
for i := 0; i < len(verifications.Kvs); i++ {
verification := &Verification{}
- err = json.Unmarshal(verifications.Kvs[i].Value, &verification)
+ err = json.Unmarshal(verifications.Kvs[i].Value.([]byte), &verification)
if err != nil {
PactLogger.Errorf(nil, "verification result publish request failed, verification result unmarshall error.")
return &PublishVerificationResponse{
diff --git a/server/broker/store.go b/server/broker/store.go
index 51acacaf..cb73923a 100644
--- a/server/broker/store.go
+++ b/server/broker/store.go
@@ -33,44 +33,44 @@ var (
var brokerKvStore = &BKvStore{}
func init() {
- PARTICIPANT = backend.Store().MustInstall(backend.NewEntity("PARTICIPANT", GetBrokerParticipantKey("")))
- VERSION = backend.Store().MustInstall(backend.NewEntity("VERSION", GetBrokerVersionKey("")))
- PACT = backend.Store().MustInstall(backend.NewEntity("PACT", GetBrokerPactKey("")))
- PACT_VERSION = backend.Store().MustInstall(backend.NewEntity("PACT_VERSION", GetBrokerPactVersionKey("")))
- PACT_TAG = backend.Store().MustInstall(backend.NewEntity("PACT_TAG", GetBrokerTagKey("")))
- VERIFICATION = backend.Store().MustInstall(backend.NewEntity("VERIFICATION", GetBrokerVerificationKey("")))
- PACT_LATEST = backend.Store().MustInstall(backend.NewEntity("PACT_LATEST", GetBrokerLatestKey("")))
+ PARTICIPANT = backend.Store().MustInstall(backend.NewEntity("PARTICIPANT", backend.DefaultConfig().WithPrefix(GetBrokerParticipantKey(""))))
+ VERSION = backend.Store().MustInstall(backend.NewEntity("VERSION", backend.DefaultConfig().WithPrefix(GetBrokerVersionKey(""))))
+ PACT = backend.Store().MustInstall(backend.NewEntity("PACT", backend.DefaultConfig().WithPrefix(GetBrokerPactKey(""))))
+ PACT_VERSION = backend.Store().MustInstall(backend.NewEntity("PACT_VERSION", backend.DefaultConfig().WithPrefix(GetBrokerPactVersionKey(""))))
+ PACT_TAG = backend.Store().MustInstall(backend.NewEntity("PACT_TAG", backend.DefaultConfig().WithPrefix(GetBrokerTagKey(""))))
+ VERIFICATION = backend.Store().MustInstall(backend.NewEntity("VERIFICATION", backend.DefaultConfig().WithPrefix(GetBrokerVerificationKey(""))))
+ PACT_LATEST = backend.Store().MustInstall(backend.NewEntity("PACT_LATEST", backend.DefaultConfig().WithPrefix(GetBrokerLatestKey(""))))
}
type BKvStore struct {
}
-func (s *BKvStore) Participant() *backend.Indexer {
+func (s *BKvStore) Participant() backend.Indexer {
return backend.Store().Entity(PARTICIPANT)
}
-func (s *BKvStore) Version() *backend.Indexer {
+func (s *BKvStore) Version() backend.Indexer {
return backend.Store().Entity(VERSION)
}
-func (s *BKvStore) Pact() *backend.Indexer {
+func (s *BKvStore) Pact() backend.Indexer {
return backend.Store().Entity(PACT)
}
-func (s *BKvStore) PactVersion() *backend.Indexer {
+func (s *BKvStore) PactVersion() backend.Indexer {
return backend.Store().Entity(PACT_VERSION)
}
-func (s *BKvStore) PactTag() *backend.Indexer {
+func (s *BKvStore) PactTag() backend.Indexer {
return backend.Store().Entity(PACT_TAG)
}
-func (s *BKvStore) Verification() *backend.Indexer {
+func (s *BKvStore) Verification() backend.Indexer {
return backend.Store().Entity(VERIFICATION)
}
-func (s *BKvStore) PactLatest() *backend.Indexer {
+func (s *BKvStore) PactLatest() backend.Indexer {
return backend.Store().Entity(PACT_LATEST)
}
diff --git a/server/broker/util.go b/server/broker/util.go
index 895ee8ca..3d398b50 100644
--- a/server/broker/util.go
+++ b/server/broker/util.go
@@ -187,7 +187,7 @@ func GetBrokerParticipantUtils(ctx context.Context, tenant string, appId string,
}
participant := &Participant{}
- err = json.Unmarshal(participants.Kvs[0].Value, participant)
+ err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
if err != nil {
return nil, err
}
@@ -259,7 +259,7 @@ func GetParticipant(ctx context.Context, domain string, appId string,
return nil, nil
}
participant := &Participant{}
- err = json.Unmarshal(participants.Kvs[0].Value, participant)
+ err = json.Unmarshal(participants.Kvs[0].Value.([]byte), participant)
if err != nil {
return nil, err
}
@@ -278,7 +278,7 @@ func GetVersion(ctx context.Context, domain string, number string,
return nil, nil
}
version := &Version{}
- err = json.Unmarshal(versions.Kvs[0].Value, version)
+ err = json.Unmarshal(versions.Kvs[0].Value.([]byte), version)
if err != nil {
return nil, err
}
@@ -296,7 +296,7 @@ func GetPact(ctx context.Context, domain string, consumerParticipantId int32, pr
return nil, nil
}
pact := &Pact{}
- err = json.Unmarshal(versions.Kvs[0].Value, pact)
+ err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pact)
if err != nil {
return nil, err
}
@@ -315,7 +315,7 @@ func GetPactVersion(ctx context.Context, domain string, versionId int32,
return nil, nil
}
pactVersion := &PactVersion{}
- err = json.Unmarshal(versions.Kvs[0].Value, pactVersion)
+ err = json.Unmarshal(versions.Kvs[0].Value.([]byte), pactVersion)
if err != nil {
return nil, err
}
@@ -331,7 +331,7 @@ func GetData(ctx context.Context, key string) (int, error) {
if len(values.Kvs) == 0 {
return -1, nil
}
- id, err := strconv.Atoi(string(values.Kvs[0].Value))
+ id, err := strconv.Atoi(string(values.Kvs[0].Value.([]byte)))
if err != nil {
return -1, err
}
@@ -525,7 +525,7 @@ func GetLastestVersionNumberForParticipant(ctx context.Context,
order := int32(math.MinInt32)
for i := 0; i < len(versions.Kvs); i++ {
version := &Version{}
- err = json.Unmarshal(versions.Kvs[i].Value, &version)
+ err = json.Unmarshal(versions.Kvs[i].Value.([]byte), &version)
if err != nil {
return -1
}
@@ -622,7 +622,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
pactIds := make(map[int32]int32)
for i := 0; i < len(pactVersions.Kvs); i++ {
pactVersion := &PactVersion{}
- err = json.Unmarshal(pactVersions.Kvs[i].Value, pactVersion)
+ err = json.Unmarshal(pactVersions.Kvs[i].Value.([]byte), pactVersion)
if err != nil {
return nil, -1, err
}
@@ -656,7 +656,7 @@ func RetrieveProviderConsumerPact(ctx context.Context,
}
for i := 0; i < len(pacts.Kvs); i++ {
pactObj := &Pact{}
- err = json.Unmarshal(pacts.Kvs[i].Value, pactObj)
+ err = json.Unmarshal(pacts.Kvs[i].Value.([]byte), pactObj)
if err != nil {
return nil, -1, err
}
diff --git a/server/core/backend/cacher.go b/server/core/backend/cache.go
similarity index 84%
rename from server/core/backend/cacher.go
rename to server/core/backend/cache.go
index 410ef508..2f9107e1 100644
--- a/server/core/backend/cacher.go
+++ b/server/core/backend/cache.go
@@ -17,15 +17,18 @@
package backend
type Cache interface {
- Version() int64
- Data(interface{}) interface{}
- Have(interface{}) bool
+ Name() string
Size() int
+
+ Get(k string) *KeyValue
+ GetAll(prefix string, arr *[]*KeyValue) int
+ ForEach(iter func(k string, v *KeyValue) (next bool))
+
+ Put(k string, v *KeyValue)
+ Remove(k string)
}
type Cacher interface {
- // Name is the cache size metric name
- Name() string
Cache() Cache
Run()
Stop()
diff --git a/server/core/backend/cache_kv.go b/server/core/backend/cache_kv.go
index cedb9dc6..b001544d 100644
--- a/server/core/backend/cache_kv.go
+++ b/server/core/backend/cache_kv.go
@@ -17,511 +17,155 @@
package backend
import (
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
- "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- "github.com/coreos/etcd/mvcc/mvccpb"
- "golang.org/x/net/context"
+ "strings"
"sync"
"time"
)
type KvCache struct {
- owner *KvCacher
- size int
- store map[string]*mvccpb.KeyValue
+ Cfg *Config
+ name string
+ store map[string]map[string]*KeyValue
rwMux sync.RWMutex
lastRefresh time.Time
lastMaxSize int
}
-func (c *KvCache) Version() int64 {
- return c.owner.lw.Revision()
+func (c *KvCache) Name() string {
+ return c.name
}
-func (c *KvCache) Data(k interface{}) interface{} {
- c.rwMux.RLock()
- kv, ok := c.store[k.(string)]
- c.rwMux.RUnlock()
- if !ok {
- return nil
- }
- return kv
+func (c *KvCache) Size() (l int) {
+ l = c.GetAll(c.Cfg.Prefix, nil)
+ return
}
-func (c *KvCache) Have(k interface{}) (ok bool) {
+func (c *KvCache) Get(key string) (v *KeyValue) {
c.rwMux.RLock()
- _, ok = c.store[k.(string)]
+ prefix := c.prefix(key)
+ if p, ok := c.store[prefix]; ok {
+ v, _ = p[key]
+ }
c.rwMux.RUnlock()
return
}
-func (c *KvCache) RLock() map[string]*mvccpb.KeyValue {
+func (c *KvCache) GetAll(prefix string, arr *[]*KeyValue) (count int) {
c.rwMux.RLock()
- return c.store
-}
-
-func (c *KvCache) RUnlock() {
+ count = c.getPrefixKey(arr, prefix)
c.rwMux.RUnlock()
+ return
}
-func (c *KvCache) Lock() map[string]*mvccpb.KeyValue {
+func (c *KvCache) Put(key string, v *KeyValue) {
c.rwMux.Lock()
- return c.store
-}
-
-func (c *KvCache) Unlock() {
- l := len(c.store)
- if l > c.lastMaxSize {
- c.lastMaxSize = l
- }
- if c.size >= l &&
- c.lastMaxSize > c.size*DEFAULT_COMPACT_TIMES &&
- time.Now().Sub(c.lastRefresh) >= DEFAULT_COMPACT_TIMEOUT {
- c.compact()
- c.lastMaxSize = l
- c.lastRefresh = time.Now()
- }
-
+ c.addPrefixKey(key, v)
c.rwMux.Unlock()
}
-func (c *KvCache) compact() {
- // gc
- newCache := make(map[string]*mvccpb.KeyValue, c.size)
- for k, v := range c.store {
- newCache[k] = v
- }
- c.store = newCache
-
- util.Logger().Infof("cache %s is not in use over %s, compact capacity to size %d->%d",
- c.owner.Cfg.Prefix, DEFAULT_COMPACT_TIMEOUT, c.lastMaxSize, c.size)
+func (c *KvCache) Remove(key string) {
+ c.rwMux.Lock()
+ c.deletePrefixKey(key)
+ c.rwMux.Unlock()
}
-func (c *KvCache) Size() (l int) {
+func (c *KvCache) ForEach(iter func(k string, v *KeyValue) (next bool)) {
c.rwMux.RLock()
- l = len(c.store)
- c.rwMux.RUnlock()
- return
-}
-
-type KvCacher struct {
- Cfg Config
-
- name string
- lastRev int64
- noEventCount int
-
- ready chan struct{}
- lw ListWatcher
- mux sync.Mutex
- once sync.Once
- cache *KvCache
- goroutine *util.GoRoutine
-}
-
-func (c *KvCacher) Name() string {
- return c.name
-}
-
-func (c *KvCacher) needList() bool {
- rev := c.lw.Revision()
- defer func() { c.lastRev = rev }()
-
- if rev == 0 {
- c.noEventCount = 0
- return true
- }
- if c.lastRev != rev {
- c.noEventCount = 0
- return false
- }
- c.noEventCount++
- if c.noEventCount < c.Cfg.NoEventMaxInterval {
- return false
- }
-
- util.Logger().Debugf("no events come in more then %s, need to list key %s, rev: %d",
- time.Duration(c.noEventCount)*c.Cfg.Timeout, c.Cfg.Prefix, rev)
- c.noEventCount = 0
- return true
-}
-
-func (c *KvCacher) doList(cfg ListWatchConfig) error {
- kvs, err := c.lw.List(cfg)
- if err != nil {
- return err
- }
-
- start := time.Now()
- evts := c.filter(c.lw.Revision(), kvs)
- if ec, kc := len(evts), len(kvs); c.Cfg.DeferHandler != nil && ec == 0 && kc != 0 &&
- c.Cfg.DeferHandler.Reset() {
- util.Logger().Warnf(nil, "most of the protected data(%d/%d) are recovered", kc, c.cache.Size())
- }
- c.sync(evts)
- util.LogDebugOrWarnf(start, "finish to cache key %s, %d items, rev: %d",
- c.Cfg.Prefix, len(kvs), c.lw.Revision())
-
- return nil
-}
-
-func (c *KvCacher) doWatch(cfg ListWatchConfig) error {
- watcher := c.lw.Watch(cfg)
- return c.handleWatcher(watcher)
-}
-
-func (c *KvCacher) ListAndWatch(ctx context.Context) error {
- c.mux.Lock()
- defer c.mux.Unlock()
-
- cfg := ListWatchConfig{
- Timeout: c.Cfg.Timeout,
- Context: ctx,
- }
- if c.needList() {
- c.doList(cfg)
- }
-
- util.SafeCloseChan(c.ready)
-
- return c.doWatch(cfg)
-}
-
-func (c *KvCacher) handleWatcher(watcher *Watcher) error {
- defer watcher.Stop()
- for evts := range watcher.EventBus() {
- if evts[0].Type == proto.EVT_ERROR {
- err := evts[0].Object.(error)
- return err
- }
- c.sync(evts)
- }
- return nil
-}
-
-func (c *KvCacher) needDeferHandle(evts []KvEvent) bool {
- if c.Cfg.DeferHandler == nil || !c.IsReady() {
- return false
- }
-
- return c.Cfg.DeferHandler.OnCondition(c.Cache(), evts)
-}
-
-func (c *KvCacher) refresh(ctx context.Context) {
- util.Logger().Debugf("start to list and watch %s", c.Cfg)
- retries := 0
- ticker := time.NewTicker(DEFAULT_METRICS_INTERVAL)
- for {
- nextPeriod := minWaitInterval
- if err := c.ListAndWatch(ctx); err != nil {
- nextPeriod = util.GetBackoff().Delay(retries)
- retries++
- } else {
- retries = 0
- }
-
- timer := time.NewTimer(nextPeriod)
- select {
- case <-ctx.Done():
- timer.Stop()
-
- util.Logger().Debugf("stop to list and watch %s", c.Cfg)
- return
- case <-ticker.C:
- timer.Stop()
-
- ReportCacheMetrics(c.Name(), "raw", c.cache.RLock())
- c.cache.RUnlock()
- case <-timer.C:
- }
- }
-}
-
-func (c *KvCacher) deferHandle(ctx context.Context) {
- if c.Cfg.DeferHandler == nil {
- return
- }
-
- var (
- evts = make([]KvEvent, eventBlockSize)
- i int
- )
- for {
- timer := time.NewTimer(300 * time.Millisecond)
- select {
- case <-ctx.Done():
- timer.Stop()
- return
- case evt, ok := <-c.Cfg.DeferHandler.HandleChan():
- timer.Stop()
-
- if !ok {
- <-time.After(time.Second)
- continue
+loopParent:
+ for _, p := range c.store {
+ for k, v := range p {
+ if v == nil {
+ continue loopParent
}
-
- if i >= eventBlockSize {
- c.onEvents(evts[:i])
- evts = make([]KvEvent, eventBlockSize)
- i = 0
- }
-
- evts[i] = evt
- i++
- case <-timer.C:
- if i == 0 {
- continue
+ if !iter(k, v) {
+ break loopParent
}
-
- c.onEvents(evts[:i])
- evts = make([]KvEvent, eventBlockSize)
- i = 0
}
}
+ c.rwMux.RUnlock()
}
-func (c *KvCacher) sync(evts []KvEvent) {
- if len(evts) == 0 {
- return
+func (c *KvCache) prefix(key string) string {
+ if len(key) == 0 {
+ return ""
}
-
- if c.needDeferHandle(evts) {
- return
- }
-
- c.onEvents(evts)
+ return key[:strings.LastIndex(key[:len(key)-1], "/")+1]
}
-func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
- store := c.cache.RLock()
- defer c.cache.RUnlock()
-
- oc, nc := len(store), len(items)
- tc := oc + nc
- if tc == 0 {
- return nil
- }
- max := oc
- if nc > oc {
- max = nc
- }
-
- newStore := make(map[string]*mvccpb.KeyValue, nc)
- for _, kv := range items {
- newStore[util.BytesToStringWithNoCopy(kv.Key)] = kv
- }
- filterStopCh := make(chan struct{})
- eventsCh := make(chan [eventBlockSize]KvEvent, 2)
-
- go c.filterDelete(store, newStore, rev, eventsCh, filterStopCh)
-
- go c.filterCreateOrUpdate(store, newStore, rev, eventsCh, filterStopCh)
-
- evts := make([]KvEvent, 0, max)
- for block := range eventsCh {
- for _, e := range block {
- if e.Object == nil {
- break
+func (c *KvCache) getPrefixKey(arr *[]*KeyValue, prefix string) (count int) {
+ keysRef, ok := c.store[prefix]
+ if !ok {
+ return 0
+ }
+
+ for key := range keysRef {
+ n := c.getPrefixKey(arr, key)
+ if n == 0 {
+ count += len(keysRef)
+ if arr != nil {
+ // TODO support sort option
+ for _, v := range keysRef {
+ *arr = append(*arr, v)
+ }
}
- evts = append(evts, e)
+ break
}
+ count += n
}
- return evts
+ return count
}
-func (c *KvCacher) filterDelete(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
- rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
- var block [eventBlockSize]KvEvent
- i := 0
- for k, v := range store {
- _, ok := newStore[k]
- if ok {
- continue
- }
-
- if i >= eventBlockSize {
- eventsCh <- block
- block = [eventBlockSize]KvEvent{}
- i = 0
- }
-
- block[i] = KvEvent{
- Revision: rev,
- Type: proto.EVT_DELETE,
- Prefix: c.Cfg.Prefix,
- Object: v,
- }
- i++
- }
-
- if i > 0 {
- eventsCh <- block
- }
-
- close(filterStopCh)
-}
-
-func (c *KvCacher) filterCreateOrUpdate(store map[string]*mvccpb.KeyValue, newStore map[string]*mvccpb.KeyValue,
- rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
- var block [eventBlockSize]KvEvent
- i := 0
- for k, v := range newStore {
- ov, ok := store[k]
- if !ok {
- if i >= eventBlockSize {
- eventsCh <- block
- block = [eventBlockSize]KvEvent{}
- i = 0
- }
-
- block[i] = KvEvent{
- Revision: rev,
- Type: proto.EVT_CREATE,
- Prefix: c.Cfg.Prefix,
- Object: v,
- }
- i++
- continue
- }
-
- if ov.CreateRevision == v.CreateRevision && ov.ModRevision == v.ModRevision {
- continue
- }
-
- if i >= eventBlockSize {
- eventsCh <- block
- block = [eventBlockSize]KvEvent{}
- i = 0
- }
-
- block[i] = KvEvent{
- Revision: rev,
- Type: proto.EVT_UPDATE,
- Prefix: c.Cfg.Prefix,
- Object: v,
- }
- i++
+func (c *KvCache) addPrefixKey(key string, val *KeyValue) {
+ if len(c.Cfg.Prefix) >= len(key) {
+ return
}
-
- if i > 0 {
- eventsCh <- block
+ prefix := c.prefix(key)
+ if len(prefix) == 0 {
+ return
}
-
- <-filterStopCh
-
- close(eventsCh)
-}
-
-func (c *KvCacher) onEvents(evts []KvEvent) {
- init := !c.IsReady()
- store := c.cache.Lock()
- for i, evt := range evts {
- kv := evt.Object.(*mvccpb.KeyValue)
- key := util.BytesToStringWithNoCopy(kv.Key)
- prevKv, ok := store[key]
-
- switch evt.Type {
- case proto.EVT_CREATE, proto.EVT_UPDATE:
- switch {
- case init:
- evt.Type = proto.EVT_INIT
- case !ok && evt.Type != proto.EVT_CREATE:
- util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
- evt.Type, proto.EVT_CREATE, key)
- evt.Type = proto.EVT_CREATE
- case ok && evt.Type != proto.EVT_UPDATE:
- util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
- evt.Type, proto.EVT_UPDATE, key)
- evt.Type = proto.EVT_UPDATE
- }
-
- store[key] = kv
- evts[i] = evt
- case proto.EVT_DELETE:
- if !ok {
- util.Logger().Warnf(nil, "unexpected %s event! key %s does not cache", evt.Type, key)
- } else {
- delete(store, key)
- }
- evt.Object = prevKv // maybe nil
- evts[i] = evt
+ keys, ok := c.store[prefix]
+ if !ok {
+ // build parent index key and new child nodes
+ keys = make(map[string]*KeyValue)
+ c.store[prefix] = keys
+ } else if _, ok := keys[key]; ok {
+ if val != nil {
+ // override the value
+ keys[key] = val
}
+ return
}
- c.cache.Unlock()
-
- c.onKvEvents(evts)
+ keys[key], key = val, prefix
+ c.addPrefixKey(key, nil)
}
-func (c *KvCacher) onKvEvents(evts []KvEvent) {
- if c.Cfg.OnEvent == nil {
+func (c *KvCache) deletePrefixKey(key string) {
+ prefix := c.prefix(key)
+ m, ok := c.store[prefix]
+ if !ok {
return
}
- defer util.RecoverAndReport()
+ delete(m, key)
- for _, evt := range evts {
- if evt.Object == nil {
- continue
- }
- c.Cfg.OnEvent(evt)
+ // remove parent which has no child
+ if len(m) == 0 {
+ delete(c.store, prefix)
+ c.deletePrefixKey(prefix)
}
}
-func (c *KvCacher) Cache() Cache {
- return c.cache
-}
-
-func (c *KvCacher) Run() {
- c.once.Do(func() {
- c.goroutine.Do(c.refresh)
- c.goroutine.Do(c.deferHandle)
- })
-}
-
-func (c *KvCacher) Stop() {
- c.goroutine.Close(true)
-
- util.SafeCloseChan(c.ready)
-}
-
-func (c *KvCacher) Ready() <-chan struct{} {
- return c.ready
-}
-
-func (c *KvCacher) IsReady() bool {
- select {
- case <-c.ready:
- return true
- default:
- return false
- }
+func (c *KvCache) ReportMetrics() {
+ c.rwMux.RLock()
+ ReportCacheMetrics(c.Name(), "raw", c.store)
+ c.rwMux.RUnlock()
}
-func NewKvCache(c *KvCacher, size int) *KvCache {
+func NewKvCache(name string, cfg *Config) *KvCache {
return &KvCache{
- owner: c,
- size: size,
- store: make(map[string]*mvccpb.KeyValue, size),
+ Cfg: cfg,
+ name: name,
+ store: make(map[string]map[string]*KeyValue),
lastRefresh: time.Now(),
}
}
-
-func NewKvCacher(name string, opts ...ConfigOption) *KvCacher {
- cfg := DefaultConfig()
- for _, opt := range opts {
- opt(&cfg)
- }
-
- cacher := &KvCacher{
- name: name,
- Cfg: cfg,
- ready: make(chan struct{}),
- lw: ListWatcher{
- Client: Registry(),
- Prefix: cfg.Prefix,
- },
- goroutine: util.NewGo(context.Background()),
- }
- cacher.cache = NewKvCache(cacher, cfg.InitSize)
- return cacher
-}
diff --git a/server/core/backend/cache_null.go b/server/core/backend/cache_null.go
index d6681aa5..cce930e2 100644
--- a/server/core/backend/cache_null.go
+++ b/server/core/backend/cache_null.go
@@ -19,47 +19,27 @@ package backend
var (
NullCache = &nullCache{}
NullCacher = &nullCacher{}
- closedCh = make(chan struct{})
)
-func init() {
- close(closedCh)
-}
-
type nullCache struct {
}
-func (n *nullCache) Version() int64 {
- return 0
-}
-
-func (n *nullCache) Data(interface{}) interface{} {
- return nil
-}
-
-func (n *nullCache) Have(interface{}) bool {
- return false
-}
-
-func (n *nullCache) Size() int {
- return 0
-}
+func (n *nullCache) Name() string { return "null" }
+func (n *nullCache) Size() int { return 0 }
+func (n *nullCache) RLock() {}
+func (n *nullCache) RUnlock() {}
+func (n *nullCache) Get(k string) *KeyValue { return nil }
+func (n *nullCache) GetAll(prefix string, arr *[]*KeyValue) int { return 0 }
+func (n *nullCache) ForEach(iter func(k string, v *KeyValue) (next bool)) {}
+func (n *nullCache) Lock() {}
+func (n *nullCache) Unlock() {}
+func (n *nullCache) Put(k string, v *KeyValue) {}
+func (n *nullCache) Remove(k string) {}
type nullCacher struct {
}
-func (n *nullCacher) Name() string {
- return ""
-}
-
-func (n *nullCacher) Cache() Cache {
- return NullCache
-}
-
-func (n *nullCacher) Run() {}
-
-func (n *nullCacher) Stop() {}
-
-func (n *nullCacher) Ready() <-chan struct{} {
- return closedCh
-}
+func (n *nullCacher) Cache() Cache { return NullCache }
+func (n *nullCacher) Run() {}
+func (n *nullCacher) Stop() {}
+func (n *nullCacher) Ready() <-chan struct{} { return closedCh }
diff --git a/server/core/backend/cacher_test.go b/server/core/backend/cache_test.go
similarity index 56%
rename from server/core/backend/cacher_test.go
rename to server/core/backend/cache_test.go
index 9e06f6dd..e15ba9c3 100644
--- a/server/core/backend/cacher_test.go
+++ b/server/core/backend/cache_test.go
@@ -35,10 +35,10 @@ func BenchmarkFilter(b *testing.B) {
}
v, _ := json.Marshal(inst)
- cacher := &KvCacher{}
+ cfg := DefaultConfig().WithParser(InstanceParser)
n := 300 * 1000 // 30w
- cache := NewKvCache(cacher, n)
+ cache := NewKvCache("test", cfg)
items := make([]*mvccpb.KeyValue, 0, n)
for ; n > 0; n-- {
k := fmt.Sprintf("/%d", n)
@@ -51,11 +51,11 @@ func BenchmarkFilter(b *testing.B) {
})
} else if n > 100*1000 && n <= 20*1000 {
// update
- cache.store[k] = &mvccpb.KeyValue{
+ cache.Put(k, &KeyValue{
Key: util.StringToBytesWithNoCopy(k),
- Value: v,
+ Value: inst,
ModRevision: 1,
- }
+ })
items = append(items, &mvccpb.KeyValue{
Key: util.StringToBytesWithNoCopy(k),
Value: v,
@@ -63,13 +63,14 @@ func BenchmarkFilter(b *testing.B) {
})
} else {
// delete
- cache.store[k] = &mvccpb.KeyValue{
+ cache.Put(k, &KeyValue{
Key: util.StringToBytesWithNoCopy(k),
- Value: v,
+ Value: inst,
ModRevision: 1,
- }
+ })
}
}
+ cacher := &KvCacher{Cfg: cfg}
cacher.cache = cache
b.ResetTimer()
@@ -79,6 +80,61 @@ func BenchmarkFilter(b *testing.B) {
b.ReportAllocs()
// TODO bad performance!!!
- //10 120612060 ns/op 37128035 B/op 134 allocs/op
- //
+ //20 82367261 ns/op 37964987 B/op 80132 allocs/op
+}
+
+func TestKvCache_Get(t *testing.T) {
+ c := NewKvCache("test", DefaultConfig())
+ c.Put("", &KeyValue{Version: 1})
+ c.Put("/", &KeyValue{Version: 1})
+ c.Put("/a/b/c/d/e/1", &KeyValue{Version: 1})
+ c.Put("/a/b/c/d/e/2", &KeyValue{Version: 2})
+ c.Put("/a/b/d/d/f/3", &KeyValue{Version: 3})
+ c.Put("/a/b/e/d/g/4", &KeyValue{Version: 4})
+
+ if l := c.Size(); l != 4 {
+ t.Fatalf("TestKvCache Size() failed, %d", l)
+ }
+
+ if kv := c.Get("/a/b/c/d/e/2"); kv == nil || kv.Version != 2 {
+ t.Fatalf("TestKvCache Get() failed, %v", kv)
+ }
+
+ if l := c.GetAll("/", nil); l != 4 {
+ t.Fatalf("TestKvCache GetAll() failed, %d", l)
+ }
+
+ var arr []*KeyValue
+ if l := c.GetAll("/a/b/c/", &arr); l != 2 || (arr[0].Version != 1 && arr[1].Version != 1) {
+ t.Fatalf("TestKvCache GetAll() failed, %d, %v", l, arr)
+ }
+
+ l, b := -1, false
+ c.ForEach(func(k string, v *KeyValue) (next bool) {
+ next = false
+ l++
+ return
+ })
+ if l != 0 {
+ t.Fatalf("TestKvCache ForEach() failed, %d", l)
+ }
+ c.ForEach(func(k string, v *KeyValue) (next bool) {
+ next = true
+ l++
+ if v.Version == 4 {
+ b = true
+ }
+ return
+ })
+ if l != 4 || !b {
+ t.Fatalf("TestKvCache ForEach() failed, %d, %v", l, b)
+ }
+
+ c.Remove("")
+ c.Remove("/")
+ c.Remove("/a/b/c/d/e/2")
+ c.Remove("/a/b/d/d/f/3")
+ if l := c.Size(); l != 2 {
+ t.Fatalf("TestKvCache Size() failed, %d", l)
+ }
}
diff --git a/server/core/backend/cacher_kv.go b/server/core/backend/cacher_kv.go
new file mode 100644
index 00000000..6c6aa996
--- /dev/null
+++ b/server/core/backend/cacher_kv.go
@@ -0,0 +1,479 @@
+/*
+ * 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 backend
+
+import (
+ "errors"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "github.com/apache/incubator-servicecomb-service-center/server/core"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+ "github.com/coreos/etcd/mvcc/mvccpb"
+ "golang.org/x/net/context"
+ "sync"
+ "time"
+)
+
+type KvCacher struct {
+ Cfg *Config
+
+ lastRev int64
+ noEventCount int
+
+ ready chan struct{}
+ lw ListWatcher
+ mux sync.Mutex
+ once sync.Once
+ cache *KvCache
+ goroutine *util.GoRoutine
+}
+
+func (c *KvCacher) needList() bool {
+ rev := c.lw.Revision()
+ defer func() { c.lastRev = rev }()
+
+ if rev == 0 {
+ c.noEventCount = 0
+ return true
+ }
+ if c.lastRev != rev {
+ c.noEventCount = 0
+ return false
+ }
+ c.noEventCount++
+ if c.noEventCount < c.Cfg.NoEventMaxInterval {
+ return false
+ }
+
+ util.Logger().Debugf("no events come in more then %s, need to list key %s, rev: %d",
+ time.Duration(c.noEventCount)*c.Cfg.Timeout, c.Cfg.Prefix, rev)
+ c.noEventCount = 0
+ return true
+}
+
+func (c *KvCacher) doList(cfg ListWatchConfig) error {
+ resp, err := c.lw.List(cfg)
+ if err != nil {
+ return err
+ }
+
+ kvs := resp.Kvs
+ start := time.Now()
+ evts := c.filter(c.lw.Revision(), kvs)
+ if ec, kc := len(evts), len(kvs); c.Cfg.DeferHandler != nil && ec == 0 && kc != 0 &&
+ c.Cfg.DeferHandler.Reset() {
+ util.Logger().Warnf(nil, "most of the protected data(%d/%d) are recovered", kc, c.cache.Size())
+ }
+ c.sync(evts)
+ util.LogDebugOrWarnf(start, "finish to cache key %s, %d items, rev: %d",
+ c.Cfg.Prefix, len(kvs), c.lw.Revision())
+
+ return nil
+}
+
+func (c *KvCacher) doWatch(cfg ListWatchConfig) error {
+ watcher := c.lw.Watch(cfg)
+ return c.handleWatcher(watcher)
+}
+
+func (c *KvCacher) ListAndWatch(ctx context.Context) error {
+ c.mux.Lock()
+ defer c.mux.Unlock()
+
+ cfg := ListWatchConfig{
+ Timeout: c.Cfg.Timeout,
+ Context: ctx,
+ }
+ if c.needList() {
+ c.doList(cfg)
+ }
+
+ util.SafeCloseChan(c.ready)
+
+ return c.doWatch(cfg)
+}
+
+func (c *KvCacher) handleWatcher(watcher *Watcher) error {
+ defer watcher.Stop()
+ for resp := range watcher.EventBus() {
+ if resp == nil {
+ return errors.New("handle watcher error")
+ }
+
+ start := time.Now()
+ evts := make([]KvEvent, 0, len(resp.Kvs))
+ for _, kv := range resp.Kvs {
+ evt := KvEvent{Prefix: c.lw.Prefix, Revision: kv.ModRevision}
+ switch {
+ case resp.Action == registry.Put && kv.Version == 1:
+ evt.Type, evt.KV = proto.EVT_CREATE, c.doParse(kv)
+ case resp.Action == registry.Put:
+ evt.Type, evt.KV = proto.EVT_UPDATE, c.doParse(kv)
+ case resp.Action == registry.Delete:
+ evt.Type = proto.EVT_DELETE
+ if kv.Value == nil {
+ // it will happen in embed mode, and then need to get the cache value to unmarshal
+ evt.KV = c.cache.Get(util.BytesToStringWithNoCopy(kv.Key))
+ } else {
+ evt.KV = c.doParse(kv)
+ }
+ default:
+ util.Logger().Errorf(nil, "unknown KeyValue %v", kv)
+ continue
+ }
+ if evt.KV == nil {
+ continue
+ }
+ evts = append(evts, evt)
+ }
+ c.sync(evts)
+ util.LogDebugOrWarnf(start, "finish to handle %d events, prefix: %s, rev: %d",
+ len(evts), c.Cfg.Prefix, c.lw.Revision())
+ }
+ return nil
+}
+
+func (c *KvCacher) needDeferHandle(evts []KvEvent) bool {
+ if c.Cfg.DeferHandler == nil || !c.IsReady() {
+ return false
+ }
+
+ return c.Cfg.DeferHandler.OnCondition(c.Cache(), evts)
+}
+
+func (c *KvCacher) refresh(ctx context.Context) {
+ util.Logger().Debugf("start to list and watch %s", c.Cfg)
+ retries := 0
+
+ timer := time.NewTimer(minWaitInterval)
+ defer timer.Stop()
+ for {
+ nextPeriod := minWaitInterval
+ if err := c.ListAndWatch(ctx); err != nil {
+ nextPeriod = util.GetBackoff().Delay(retries)
+ retries++
+ } else {
+ retries = 0
+ }
+
+ select {
+ case <-ctx.Done():
+ util.Logger().Debugf("stop to list and watch %s", c.Cfg)
+ return
+ case <-timer.C:
+ timer.Reset(nextPeriod)
+ }
+ }
+}
+
+// keep the evts valid when call sync
+func (c *KvCacher) sync(evts []KvEvent) {
+ if len(evts) == 0 {
+ return
+ }
+
+ if c.needDeferHandle(evts) {
+ return
+ }
+
+ c.onEvents(evts)
+}
+
+func (c *KvCacher) filter(rev int64, items []*mvccpb.KeyValue) []KvEvent {
+ nc := len(items)
+ newStore := make(map[string]*mvccpb.KeyValue, nc)
+ for _, kv := range items {
+ newStore[util.BytesToStringWithNoCopy(kv.Key)] = kv
+ }
+ filterStopCh := make(chan struct{})
+ eventsCh := make(chan [eventBlockSize]KvEvent, 2)
+
+ go c.filterDelete(newStore, rev, eventsCh, filterStopCh)
+
+ go c.filterCreateOrUpdate(newStore, rev, eventsCh, filterStopCh)
+
+ evts := make([]KvEvent, 0, nc)
+ for block := range eventsCh {
+ for _, e := range block {
+ if e.KV == nil {
+ break
+ }
+ evts = append(evts, e)
+ }
+ }
+ return evts
+}
+
+func (c *KvCacher) filterDelete(newStore map[string]*mvccpb.KeyValue,
+ rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
+ var block [eventBlockSize]KvEvent
+ i := 0
+
+ c.cache.ForEach(func(k string, v *KeyValue) (next bool) {
+ next = true
+
+ _, ok := newStore[k]
+ if ok {
+ return
+ }
+
+ if i >= eventBlockSize {
+ eventsCh <- block
+ block = [eventBlockSize]KvEvent{}
+ i = 0
+ }
+
+ block[i] = KvEvent{
+ Revision: rev,
+ Type: proto.EVT_DELETE,
+ Prefix: c.Cfg.Prefix,
+ KV: v,
+ }
+ i++
+ return
+ })
+
+ if i > 0 {
+ eventsCh <- block
+ }
+
+ close(filterStopCh)
+}
+
+func (c *KvCacher) filterCreateOrUpdate(newStore map[string]*mvccpb.KeyValue,
+ rev int64, eventsCh chan [eventBlockSize]KvEvent, filterStopCh chan struct{}) {
+ var block [eventBlockSize]KvEvent
+ i := 0
+
+ for k, v := range newStore {
+ ov := c.cache.Get(k)
+ if ov == nil {
+ if i >= eventBlockSize {
+ eventsCh <- block
+ block = [eventBlockSize]KvEvent{}
+ i = 0
+ }
+
+ if kv := c.doParse(v); kv != nil {
+ block[i] = KvEvent{
+ Revision: rev,
+ Type: proto.EVT_CREATE,
+ Prefix: c.Cfg.Prefix,
+ KV: kv,
+ }
+ i++
+ }
+ continue
+ }
+
+ if ov.CreateRevision == v.CreateRevision && ov.ModRevision == v.ModRevision {
+ continue
+ }
+
+ if i >= eventBlockSize {
+ eventsCh <- block
+ block = [eventBlockSize]KvEvent{}
+ i = 0
+ }
+
+ if kv := c.doParse(v); kv != nil {
+ block[i] = KvEvent{
+ Revision: rev,
+ Type: proto.EVT_UPDATE,
+ Prefix: c.Cfg.Prefix,
+ KV: kv,
+ }
+ i++
+ }
+ }
+
+ if i > 0 {
+ eventsCh <- block
+ }
+
+ <-filterStopCh
+
+ close(eventsCh)
+}
+
+func (c *KvCacher) deferHandle(ctx context.Context) {
+ if c.Cfg.DeferHandler == nil {
+ return
+ }
+
+ var (
+ evts = make([]KvEvent, eventBlockSize)
+ i int
+ )
+ interval := 300 * time.Millisecond
+ timer := time.NewTimer(interval)
+ defer timer.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case evt, ok := <-c.Cfg.DeferHandler.HandleChan():
+ if !ok {
+ return
+ }
+
+ if i >= eventBlockSize {
+ c.onEvents(evts[:i])
+ evts = make([]KvEvent, eventBlockSize)
+ i = 0
+ }
+
+ evts[i] = evt
+ i++
+
+ util.ResetTimer(timer, interval)
+ case <-timer.C:
+ timer.Reset(interval)
+
+ if i == 0 {
+ continue
+ }
+
+ c.onEvents(evts[:i])
+ evts = make([]KvEvent, eventBlockSize)
+ i = 0
+ }
+ }
+}
+
+func (c *KvCacher) onEvents(evts []KvEvent) {
+ init := !c.IsReady()
+ for i, evt := range evts {
+ key := util.BytesToStringWithNoCopy(evt.KV.Key)
+ prevKv := c.cache.Get(key)
+ ok := prevKv != nil
+
+ switch evt.Type {
+ case proto.EVT_CREATE, proto.EVT_UPDATE:
+ switch {
+ case init:
+ evt.Type = proto.EVT_INIT
+ case !ok && evt.Type != proto.EVT_CREATE:
+ util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
+ evt.Type, proto.EVT_CREATE, key)
+ evt.Type = proto.EVT_CREATE
+ case ok && evt.Type != proto.EVT_UPDATE:
+ util.Logger().Warnf(nil, "unexpected %s event! it should be %s key %s",
+ evt.Type, proto.EVT_UPDATE, key)
+ evt.Type = proto.EVT_UPDATE
+ }
+
+ c.cache.Put(key, evt.KV)
+ evts[i] = evt
+ case proto.EVT_DELETE:
+ if !ok {
+ util.Logger().Warnf(nil, "unexpected %s event! key %s does not cache",
+ evt.Type, key)
+ } else {
+ evt.KV = prevKv
+ c.cache.Remove(key)
+ }
+ evts[i] = evt
+ }
+ }
+
+ c.onKvEvents(evts)
+}
+
+func (c *KvCacher) onKvEvents(evts []KvEvent) {
+ if c.Cfg.OnEvent == nil {
+ return
+ }
+
+ defer util.RecoverAndReport()
+
+ for _, evt := range evts {
+ c.Cfg.OnEvent(evt)
+ }
+}
+
+func (c *KvCacher) doParse(src *mvccpb.KeyValue) (kv *KeyValue) {
+ kv = new(KeyValue)
+ if err := kv.From(c.Cfg.Parser, src); err != nil {
+ util.Logger().Errorf(err, "parse %s value failed", util.BytesToStringWithNoCopy(src.Key))
+ }
+ return kv
+}
+
+func (c *KvCacher) Cache() Cache {
+ return c.cache
+}
+
+func (c *KvCacher) Run() {
+ c.once.Do(func() {
+ c.goroutine.Do(c.refresh)
+ c.goroutine.Do(c.deferHandle)
+ c.goroutine.Do(c.reportMetrics)
+ })
+}
+
+func (c *KvCacher) Stop() {
+ c.goroutine.Close(true)
+
+ util.SafeCloseChan(c.ready)
+}
+
+func (c *KvCacher) Ready() <-chan struct{} {
+ return c.ready
+}
+
+func (c *KvCacher) IsReady() bool {
+ select {
+ case <-c.ready:
+ return true
+ default:
+ return false
+ }
+}
+
+func (c *KvCacher) reportMetrics(ctx context.Context) {
+ if !core.ServerInfo.Config.EnablePProf ||
+ !core.ServerInfo.Config.EnableCache {
+ return
+ }
+ timer := time.NewTimer(DEFAULT_METRICS_INTERVAL)
+ defer timer.Stop()
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-timer.C:
+ c.cache.ReportMetrics()
+
+ timer.Reset(DEFAULT_METRICS_INTERVAL)
+ }
+ }
+}
+
+func NewKvCacher(name string, cfg *Config) *KvCacher {
+ cacher := &KvCacher{
+ Cfg: cfg,
+ ready: make(chan struct{}),
+ lw: ListWatcher{
+ Client: Registry(),
+ Prefix: cfg.Prefix,
+ },
+ goroutine: util.NewGo(context.Background()),
+ }
+ cacher.cache = NewKvCache(name, cfg)
+ return cacher
+}
diff --git a/server/core/backend/common.go b/server/core/backend/common.go
index 3895f579..1ae79d30 100644
--- a/server/core/backend/common.go
+++ b/server/core/backend/common.go
@@ -22,6 +22,36 @@ import (
"time"
)
+const (
+ // re-list etcd when there is no event coming in more than 1h(=120*30s)
+ DEFAULT_MAX_NO_EVENT_INTERVAL = 120
+ DEFAULT_LISTWATCH_TIMEOUT = 30 * time.Second
+
+ DEFAULT_SELF_PRESERVATION_PERCENT = 0.8
+ DEFAULT_CACHE_INIT_SIZE = 100
+)
+
+const (
+ DEFAULT_METRICS_INTERVAL = 30 * time.Second
+ DEFAULT_COMPACT_TIMES = 2
+ DEFAULT_COMPACT_TIMEOUT = 5 * time.Minute
+
+ minWaitInterval = 1 * time.Second
+ eventBlockSize = 1000
+)
+
+const DEFAULT_CHECK_WINDOW = 2 * time.Second // instance DELETE event will be delay.
+
+const TIME_FORMAT = "15:04:05.000"
+
+const EVENT_BUS_MAX_SIZE = 1000
+
+var closedCh = make(chan struct{})
+
+func init() {
+ close(closedCh)
+}
+
type StoreType int
func (st StoreType) String() string {
@@ -74,67 +104,50 @@ var TypeNames = []string{
typeEnd: "TYPEEND",
}
-var TypeRoots = map[StoreType]string{
- SERVICE: apt.GetServiceRootKey(""),
- INSTANCE: apt.GetInstanceRootKey(""),
- DOMAIN: apt.GetDomainRootKey() + "/",
- SCHEMA: apt.GetServiceSchemaRootKey(""),
- SCHEMA_SUMMARY: apt.GetServiceSchemaSummaryRootKey(""),
- RULE: apt.GetServiceRuleRootKey(""),
- LEASE: apt.GetInstanceLeaseRootKey(""),
- SERVICE_INDEX: apt.GetServiceIndexRootKey(""),
- SERVICE_ALIAS: apt.GetServiceAliasRootKey(""),
- SERVICE_TAG: apt.GetServiceTagRootKey(""),
- RULE_INDEX: apt.GetServiceRuleIndexRootKey(""),
- DEPENDENCY: apt.GetServiceDependencyRootKey(""),
- DEPENDENCY_RULE: apt.GetServiceDependencyRuleRootKey(""),
- DEPENDENCY_QUEUE: apt.GetServiceDependencyQueueRootKey(""),
- PROJECT: apt.GetProjectRootKey(""),
-}
+var TypeConfig = map[StoreType]*Config{
+ SERVICE: DefaultConfig().WithPrefix(apt.GetServiceRootKey("")).
+ WithInitSize(500).WithParser(ServiceParser),
-var TypeInitSize = map[StoreType]int{
- SERVICE: 500,
- INSTANCE: 1000,
- DOMAIN: 100,
- SCHEMA: 0,
- SCHEMA_SUMMARY: 100,
- RULE: 100,
- LEASE: 1000,
- SERVICE_INDEX: 500,
- SERVICE_ALIAS: 100,
- SERVICE_TAG: 100,
- RULE_INDEX: 100,
- DEPENDENCY: 100,
- DEPENDENCY_RULE: 100,
- DEPENDENCY_QUEUE: 100,
- PROJECT: 100,
-}
+ INSTANCE: DefaultConfig().WithPrefix(apt.GetInstanceRootKey("")).
+ WithInitSize(1000).WithParser(InstanceParser).
+ WithDeferHandler(NewInstanceEventDeferHandler()),
-const (
- // re-list etcd when there is no event coming in more than 2h(=240*30s)
- DEFAULT_MAX_NO_EVENT_INTERVAL = 240
- DEFAULT_LISTWATCH_TIMEOUT = 30 * time.Second
+ DOMAIN: DefaultConfig().WithPrefix(apt.GetDomainRootKey() + "/").
+ WithInitSize(100).WithParser(StringParser),
- DEFAULT_SELF_PRESERVATION_PERCENT = 0.8
- DEFAULT_CACHE_INIT_SIZE = 100
-)
+ SCHEMA: DefaultConfig().WithPrefix(apt.GetServiceSchemaRootKey("")).
+ WithInitSize(0),
-const (
- DEFAULT_METRICS_INTERVAL = 30 * time.Second
+ SCHEMA_SUMMARY: DefaultConfig().WithPrefix(apt.GetServiceSchemaSummaryRootKey("")).
+ WithInitSize(100).WithParser(StringParser),
- DEFAULT_COMPACT_TIMES = 2
- DEFAULT_COMPACT_TIMEOUT = 5 * time.Minute
- minWaitInterval = 1 * time.Second
- eventBlockSize = 1000
-)
+ RULE: DefaultConfig().WithPrefix(apt.GetServiceRuleRootKey("")).
+ WithInitSize(100).WithParser(RuleParser),
-const (
- DEFAULT_MAX_EVENT_COUNT = 1000
- DEFAULT_ADD_QUEUE_TIMEOUT = 5 * time.Second
-)
+ LEASE: DefaultConfig().WithPrefix(apt.GetInstanceLeaseRootKey("")).
+ WithInitSize(1000).WithParser(StringParser),
-const DEFAULT_CHECK_WINDOW = 2 * time.Second // instance DELETE event will be delay.
+ SERVICE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceIndexRootKey("")).
+ WithInitSize(500).WithParser(StringParser),
-const TIME_FORMAT = "15:04:05.000"
+ SERVICE_ALIAS: DefaultConfig().WithPrefix(apt.GetServiceAliasRootKey("")).
+ WithInitSize(100).WithParser(StringParser),
-const EVENT_BUS_MAX_SIZE = 1000
+ SERVICE_TAG: DefaultConfig().WithPrefix(apt.GetServiceTagRootKey("")).
+ WithInitSize(100).WithParser(MapParser),
+
+ RULE_INDEX: DefaultConfig().WithPrefix(apt.GetServiceRuleIndexRootKey("")).
+ WithInitSize(100).WithParser(StringParser),
+
+ DEPENDENCY: DefaultConfig().WithPrefix(apt.GetServiceDependencyRootKey("")).
+ WithInitSize(100),
+
+ DEPENDENCY_RULE: DefaultConfig().WithPrefix(apt.GetServiceDependencyRuleRootKey("")).
+ WithInitSize(100).WithParser(DependencyRuleParser),
+
+ DEPENDENCY_QUEUE: DefaultConfig().WithPrefix(apt.GetServiceDependencyQueueRootKey("")).
+ WithInitSize(100).WithParser(DependencyQueueParser),
+
+ PROJECT: DefaultConfig().WithPrefix(apt.GetProjectRootKey("")).
+ WithInitSize(100).WithParser(StringParser),
+}
diff --git a/server/core/backend/config.go b/server/core/backend/config.go
index 13e162d1..32c2e416 100644
--- a/server/core/backend/config.go
+++ b/server/core/backend/config.go
@@ -28,48 +28,71 @@ type Config struct {
NoEventMaxInterval int
Timeout time.Duration
Period time.Duration
- OnEvent KvEventFunc
DeferHandler DeferHandler
+ OnEvent KvEventFunc
+ Parser *Parser
}
-func (cfg Config) String() string {
+func (cfg *Config) String() string {
return fmt.Sprintf("{prefix: %s, timeout: %s, period: %s}",
cfg.Prefix, cfg.Timeout, cfg.Period)
}
-type ConfigOption func(*Config)
+func (cfg *Config) WithPrefix(key string) *Config {
+ cfg.Prefix = key
+ return cfg
+}
+
+func (cfg *Config) WithInitSize(size int) *Config {
+ cfg.InitSize = size
+ return cfg
+}
-func WithPrefix(key string) ConfigOption {
- return func(cfg *Config) { cfg.Prefix = key }
+func (cfg *Config) WithTimeout(ot time.Duration) *Config {
+ cfg.Timeout = ot
+ return cfg
}
-func WithInitSize(size int) ConfigOption {
- return func(cfg *Config) { cfg.InitSize = size }
+func (cfg *Config) WithPeriod(ot time.Duration) *Config {
+ cfg.Period = ot
+ return cfg
}
-func WithTimeout(ot time.Duration) ConfigOption {
- return func(cfg *Config) { cfg.Timeout = ot }
+func (cfg *Config) WithDeferHandler(h DeferHandler) *Config {
+ cfg.DeferHandler = h
+ return cfg
}
-func WithPeriod(ot time.Duration) ConfigOption {
- return func(cfg *Config) { cfg.Period = ot }
+func (cfg *Config) WithEventFunc(f KvEventFunc) *Config {
+ cfg.OnEvent = f
+ return cfg
}
-func WithEventFunc(f KvEventFunc) ConfigOption {
- return func(cfg *Config) { cfg.OnEvent = f }
+func (cfg *Config) AppendEventFunc(f KvEventFunc) *Config {
+ if prev := cfg.OnEvent; prev != nil {
+ next := f
+ f = func(evt KvEvent) {
+ prev(evt)
+ next(evt)
+ }
+ }
+ cfg.OnEvent = f
+ return cfg
}
-func WithDeferHandler(h DeferHandler) ConfigOption {
- return func(cfg *Config) { cfg.DeferHandler = h }
+func (cfg *Config) WithParser(parser *Parser) *Config {
+ cfg.Parser = parser
+ return cfg
}
-func DefaultConfig() Config {
- return Config{
+func DefaultConfig() *Config {
+ return &Config{
Prefix: "/",
Timeout: DEFAULT_LISTWATCH_TIMEOUT,
Period: time.Second,
NoEventMaxInterval: DEFAULT_MAX_NO_EVENT_INTERVAL,
InitSize: DEFAULT_CACHE_INIT_SIZE,
+ Parser: BytesParser,
}
}
diff --git a/server/core/backend/defer_instance.go b/server/core/backend/defer_instance.go
index aab844b2..fea9485b 100644
--- a/server/core/backend/defer_instance.go
+++ b/server/core/backend/defer_instance.go
@@ -17,10 +17,8 @@
package backend
import (
- "encoding/json"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"sync"
"time"
@@ -50,7 +48,7 @@ func (iedh *InstanceEventDeferHandler) OnCondition(cache Cache, evts []KvEvent)
iedh.once.Do(func() {
iedh.cache = cache
- iedh.items = make(map[string]deferItem, eventBlockSize)
+ iedh.items = make(map[string]deferItem)
iedh.pendingCh = make(chan []KvEvent, eventBlockSize)
iedh.deferCh = make(chan KvEvent, eventBlockSize)
iedh.resetCh = make(chan struct{})
@@ -62,7 +60,7 @@ func (iedh *InstanceEventDeferHandler) OnCondition(cache Cache, evts []KvEvent)
}
func (iedh *InstanceEventDeferHandler) recoverOrDefer(evt KvEvent) error {
- kv := evt.Object.(*mvccpb.KeyValue)
+ kv := evt.KV
key := util.BytesToStringWithNoCopy(kv.Key)
_, ok := iedh.items[key]
switch evt.Type {
@@ -77,20 +75,7 @@ func (iedh *InstanceEventDeferHandler) recoverOrDefer(evt KvEvent) error {
return nil
}
- var instance pb.MicroServiceInstance
-
- // it will happen in embed mode, and then need to get the cache value to unmarshal
- if kv.Value == nil {
- if c, ok := iedh.cache.Data(key).(*mvccpb.KeyValue); ok {
- kv.Value = c.Value
- }
- }
- err := json.Unmarshal(kv.Value, &instance)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal instance file failed, key is %s, value is %s", key,
- util.BytesToStringWithNoCopy(kv.Value))
- return err
- }
+ instance := kv.Value.(*pb.MicroServiceInstance)
iedh.items[key] = deferItem{
ttl: time.NewTimer(
time.Duration(instance.HealthCheck.Interval*(instance.HealthCheck.Times+1)) * time.Second),
@@ -106,7 +91,9 @@ func (iedh *InstanceEventDeferHandler) HandleChan() <-chan KvEvent {
func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
defer util.RecoverAndReport()
+
t, n := time.NewTimer(DEFAULT_CHECK_WINDOW), false
+ defer t.Stop()
for {
select {
case <-ctx.Done():
@@ -118,10 +105,7 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
del := len(iedh.items)
if del > 0 && !n {
- if !t.Stop() {
- <-t.C
- }
- t.Reset(DEFAULT_CHECK_WINDOW)
+ util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
n = true
}
@@ -132,7 +116,6 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
del, total, iedh.Percent*100)
}
case <-t.C:
- t.Reset(DEFAULT_CHECK_WINDOW)
n = false
for key, item := range iedh.items {
@@ -151,22 +134,29 @@ func (iedh *InstanceEventDeferHandler) check(ctx context.Context) {
iedh.renew()
util.Logger().Warnf(nil, "self preservation is stopped")
}
+
+ t.Reset(DEFAULT_CHECK_WINDOW)
case <-iedh.resetCh:
iedh.renew()
util.Logger().Warnf(nil, "self preservation is reset")
+
+ util.ResetTimer(t, DEFAULT_CHECK_WINDOW)
}
}
}
func (iedh *InstanceEventDeferHandler) recover(evt KvEvent) {
- key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
+ key := util.BytesToStringWithNoCopy(evt.KV.Key)
delete(iedh.items, key)
iedh.deferCh <- evt
}
func (iedh *InstanceEventDeferHandler) renew() {
iedh.enabled = false
- iedh.items = make(map[string]deferItem, eventBlockSize)
+ for _, item := range iedh.items {
+ item.ttl.Stop()
+ }
+ iedh.items = make(map[string]deferItem)
}
func (iedh *InstanceEventDeferHandler) Reset() bool {
@@ -176,3 +166,7 @@ func (iedh *InstanceEventDeferHandler) Reset() bool {
}
return false
}
+
+func NewInstanceEventDeferHandler() *InstanceEventDeferHandler {
+ return &InstanceEventDeferHandler{Percent: DEFAULT_SELF_PRESERVATION_PERCENT}
+}
diff --git a/server/core/backend/defer_test.go b/server/core/backend/defer_test.go
index 93acd971..42a43e8b 100644
--- a/server/core/backend/defer_test.go
+++ b/server/core/backend/defer_test.go
@@ -17,11 +17,9 @@
package backend
import (
- "encoding/json"
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- "github.com/coreos/etcd/mvcc/mvccpb"
"testing"
"time"
)
@@ -42,94 +40,93 @@ func TestInstanceEventDeferHandler_OnCondition(t *testing.T) {
}
func TestInstanceEventDeferHandler_HandleChan(t *testing.T) {
- inst := &pb.MicroServiceInstance{
+ b := &pb.MicroServiceInstance{
HealthCheck: &pb.HealthCheck{
Interval: 4,
Times: 0,
},
}
- b, _ := json.Marshal(inst)
- kv1 := &mvccpb.KeyValue{
+ kv1 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/1"),
Value: b,
}
- kv2 := &mvccpb.KeyValue{
+ kv2 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/2"),
Value: b,
}
- kv3 := &mvccpb.KeyValue{
+ kv3 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/3"),
Value: b,
}
- kv4 := &mvccpb.KeyValue{
+ kv4 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/4"),
Value: b,
}
- kv5 := &mvccpb.KeyValue{
+ kv5 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/5"),
Value: b,
}
- kv6 := &mvccpb.KeyValue{
+ kv6 := &KeyValue{
Key: util.StringToBytesWithNoCopy("/6"),
Value: b,
}
- cache := NewKvCache(nil, 1)
- cache.store["/1"] = kv1
- cache.store["/2"] = kv2
- cache.store["/3"] = kv3
- cache.store["/4"] = kv4
- cache.store["/5"] = kv5
- cache.store["/6"] = kv6
+ cache := NewKvCache("test", DefaultConfig())
+ cache.Put("/1", kv1)
+ cache.Put("/2", kv2)
+ cache.Put("/3", kv3)
+ cache.Put("/4", kv4)
+ cache.Put("/5", kv5)
+ cache.Put("/6", kv6)
evts1 := []KvEvent{
{
- Type: pb.EVT_CREATE,
- Object: kv1,
+ Type: pb.EVT_CREATE,
+ KV: kv1,
},
{
- Type: pb.EVT_UPDATE,
- Object: kv1,
+ Type: pb.EVT_UPDATE,
+ KV: kv1,
},
}
evts2 := []KvEvent{
{
- Type: pb.EVT_DELETE,
- Object: kv2,
+ Type: pb.EVT_DELETE,
+ KV: kv2,
},
{
- Type: pb.EVT_DELETE,
- Object: kv3,
+ Type: pb.EVT_DELETE,
+ KV: kv3,
},
{
- Type: pb.EVT_DELETE,
- Object: kv4,
+ Type: pb.EVT_DELETE,
+ KV: kv4,
},
{
- Type: pb.EVT_DELETE,
- Object: kv5,
+ Type: pb.EVT_DELETE,
+ KV: kv5,
},
{
- Type: pb.EVT_DELETE,
- Object: kv6,
+ Type: pb.EVT_DELETE,
+ KV: kv6,
},
}
evts3 := []KvEvent{
{
- Type: pb.EVT_CREATE,
- Object: kv2,
+ Type: pb.EVT_CREATE,
+ KV: kv2,
},
{
- Type: pb.EVT_UPDATE,
- Object: kv4,
+ Type: pb.EVT_UPDATE,
+ KV: kv4,
},
{
- Type: pb.EVT_UPDATE,
- Object: kv5,
+ Type: pb.EVT_UPDATE,
+ KV: kv5,
},
{
- Type: pb.EVT_CREATE,
- Object: kv6,
+ Type: pb.EVT_CREATE,
+ KV: kv6,
},
}
@@ -159,7 +156,7 @@ func getEvents(t *testing.T, iedh *InstanceEventDeferHandler) {
select {
case evt := <-iedh.HandleChan():
fmt.Println(time.Now(), evt)
- if string(evt.Object.(*mvccpb.KeyValue).Key) == "/3" {
+ if string(evt.KV.Key) == "/3" {
evt3 = &evt
if iedh.Percent == 0.01 && evt.Type == pb.EVT_DELETE {
t.Fatalf(`TestInstanceEventDeferHandler_HandleChan with 1%% failed`)
diff --git a/server/core/backend/event.go b/server/core/backend/event.go
index 839dc7a6..720ef6f7 100644
--- a/server/core/backend/event.go
+++ b/server/core/backend/event.go
@@ -20,13 +20,11 @@ import (
"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
)
-type KvEventFunc func(evt KvEvent)
-
type KvEvent struct {
Revision int64
Type proto.EventType
Prefix string
- Object interface{}
+ KV *KeyValue
}
type KvEventHandler interface {
diff --git a/server/core/backend/event_proxy.go b/server/core/backend/event_proxy.go
index 592c70a7..3b022874 100644
--- a/server/core/backend/event_proxy.go
+++ b/server/core/backend/event_proxy.go
@@ -49,9 +49,7 @@ func (h *KvEventProxy) OnEvent(evt KvEvent) {
}
func NewEventProxy() *KvEventProxy {
- return &KvEventProxy{
- evtHandleFuncs: make([]KvEventFunc, 0, 5),
- }
+ return &KvEventProxy{}
}
func EventProxy(t StoreType) *KvEventProxy {
diff --git a/server/core/backend/extend.go b/server/core/backend/extend.go
index 200d45e5..dbc0cd14 100644
--- a/server/core/backend/extend.go
+++ b/server/core/backend/extend.go
@@ -23,26 +23,20 @@ import (
type Entity interface {
Name() string
- Prefix() string
- InitSize() int
+ Config() *Config
}
type entity struct {
- name string
- prefix string
- initSize int
+ name string
+ cfg *Config
}
func (e *entity) Name() string {
return e.name
}
-func (e *entity) Prefix() string {
- return e.prefix
-}
-
-func (e *entity) InitSize() int {
- return e.initSize
+func (e *entity) Config() *Config {
+ return e.cfg
}
func InstallType(e Entity) (id StoreType, err error) {
@@ -54,31 +48,22 @@ func InstallType(e Entity) (id StoreType, err error) {
return NONEXIST, fmt.Errorf("redeclare store type '%s'", n)
}
}
- for _, r := range TypeRoots {
- if r == e.Prefix() {
+ for _, r := range TypeConfig {
+ if r.Prefix == e.Config().Prefix {
return NONEXIST, fmt.Errorf("redeclare store root '%s'", r)
}
}
id = StoreType(len(TypeNames))
TypeNames = append(TypeNames, e.Name())
-
- TypeRoots[id] = e.Prefix()
- TypeInitSize[id] = e.InitSize()
-
+ TypeConfig[id] = e.Config()
EventProxies[id] = NewEventProxy()
return
}
-func NewEntity(name, prefix string, opts ...ConfigOption) Entity {
- cfg := DefaultConfig()
- for _, opt := range opts {
- opt(&cfg)
- }
- cfg.Prefix = prefix
+func NewEntity(name string, cfg *Config) Entity {
return &entity{
- name: name,
- prefix: cfg.Prefix,
- initSize: cfg.InitSize,
+ name: name,
+ cfg: cfg,
}
}
diff --git a/server/core/backend/extend_test.go b/server/core/backend/extend_test.go
index 6a37ce5c..3f1e1af0 100644
--- a/server/core/backend/extend_test.go
+++ b/server/core/backend/extend_test.go
@@ -25,12 +25,8 @@ func (e *extend) Name() string {
return "test"
}
-func (e *extend) Prefix() string {
- return "/test"
-}
-
-func (e *extend) InitSize() int {
- return 0
+func (e *extend) Config() *Config {
+ return DefaultConfig().WithPrefix("/test")
}
func TestInstallType(t *testing.T) {
diff --git a/server/core/backend/indexer.go b/server/core/backend/indexer.go
index a88fec7d..4f47a72d 100644
--- a/server/core/backend/indexer.go
+++ b/server/core/backend/indexer.go
@@ -17,344 +17,78 @@
package backend
import (
+ "fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
- "github.com/apache/incubator-servicecomb-service-center/server/core"
- pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"strings"
- "sync"
- "time"
)
-type Indexer struct {
- BuildTimeout time.Duration
- Root string
-
- cacher Cacher
- goroutine *util.GoRoutine
- ready chan struct{}
- prefixIndex map[string]map[string]struct{}
- prefixBuildQueue chan KvEvent
- prefixLock sync.RWMutex
- isClose bool
+type Response struct {
+ Kvs []*KeyValue
+ Count int64
}
-func (i *Indexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (*registry.PluginResponse, error) {
- op := registry.OpGet(opts...)
-
- key := util.BytesToStringWithNoCopy(op.Key)
-
- if !core.ServerInfo.Config.EnableCache ||
- op.Mode == registry.MODE_NO_CACHE ||
- op.Revision > 0 ||
- (op.Offset >= 0 && op.Limit > 0) {
- util.Logger().Debugf("search %s match special options, request etcd server, opts: %s",
- i.cacher.Name(), op)
- return Registry().Do(ctx, opts...)
- }
-
- if op.Prefix {
- resp, err := i.searchPrefixKeyWithCache(ctx, op)
- if err != nil {
- return nil, err
- }
-
- if len(resp.Kvs) > 0 || op.Mode == registry.MODE_CACHE {
- return resp, nil
+func (pr *Response) MaxModRevision() (max int64) {
+ for _, kv := range pr.Kvs {
+ if max < kv.ModRevision {
+ max = kv.ModRevision
}
-
- util.Logger().Debugf("can not find any key from %s cache with prefix, request etcd server, key: %s",
- i.cacher.Name(), key)
- return Registry().Do(ctx, opts...)
- }
-
- resp := ®istry.PluginResponse{
- Action: op.Action,
- Count: 0,
- Revision: i.Cache().Version(),
- Succeeded: true,
- }
-
- if op.CountOnly {
- if i.Cache().Have(key) {
- resp.Count = 1
- return resp, nil
- }
- if op.Mode == registry.MODE_CACHE {
- return resp, nil
- }
-
- util.Logger().Debugf("%s cache does not store this key, request etcd server, key: %s",
- i.cacher.Name(), key)
- return Registry().Do(ctx, opts...)
- }
-
- cacheData := i.Cache().Data(key)
- if cacheData == nil {
- if op.Mode == registry.MODE_CACHE {
- return resp, nil
- }
-
- util.Logger().Debugf("do not match any key in %s cache store, request etcd server, key: %s",
- i.cacher.Name(), key)
- return Registry().Do(ctx, opts...)
}
-
- resp.Count = 1
- resp.Kvs = []*mvccpb.KeyValue{cacheData.(*mvccpb.KeyValue)}
- return resp, nil
+ return
}
-func (i *Indexer) Cache() Cache {
- return i.cacher.Cache()
+type Indexer interface {
+ Search(ctx context.Context, opts ...registry.PluginOpOption) (*Response, error)
+ Run()
+ Stop()
+ Ready() <-chan struct{}
}
-func (i *Indexer) searchPrefixKeyWithCache(ctx context.Context, op registry.PluginOp) (*registry.PluginResponse, error) {
- resp := ®istry.PluginResponse{
- Action: op.Action,
- Kvs: []*mvccpb.KeyValue{},
- Count: 0,
- Revision: i.Cache().Version(),
- Succeeded: true,
- }
-
- prefix := util.BytesToStringWithNoCopy(op.Key)
-
- i.prefixLock.RLock()
- resp.Count = int64(i.getPrefixKey(nil, prefix))
- if resp.Count == 0 || op.CountOnly {
- i.prefixLock.RUnlock()
- return resp, nil
- }
-
- t := time.Now()
- keys := make([]string, 0, resp.Count)
- i.getPrefixKey(&keys, prefix)
- i.prefixLock.RUnlock()
-
- kvs := make([]*mvccpb.KeyValue, resp.Count)
- idx := 0
- for _, key := range keys {
- c := i.Cache().Data(key) // TODO too slow when big data is requested
- if c == nil {
- // it means resp.Count is not equal to len(keys)
- util.Logger().Warnf(nil, "unexpected nil cache, maybe it is removed, key is %s", key)
- continue
- }
- kvs[idx] = c.(*mvccpb.KeyValue)
- idx++
- }
- util.LogNilOrWarnf(t, "too long to copy data[%d] from cache[%d] with prefix %s", idx, len(i.prefixIndex), prefix)
-
- resp.Kvs = kvs[:idx]
- return resp, nil
+type baseIndexer struct {
+ Cfg *Config
}
-func (i *Indexer) OnCacheEvent(evt KvEvent) {
- switch evt.Type {
- case pb.EVT_INIT, pb.EVT_CREATE, pb.EVT_DELETE:
- default:
- return
- }
-
- if i.isClose {
- return
- }
- defer util.RecoverAndReport()
-
- ctx, _ := context.WithTimeout(context.Background(), i.BuildTimeout)
- select {
- case <-ctx.Done():
- key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
- util.Logger().Warnf(nil, "add event to build index queue timed out(%s), key is %s [%s] event",
- i.BuildTimeout, key, evt.Type)
- case i.prefixBuildQueue <- evt:
- }
-}
-
-func (i *Indexer) buildIndex() {
- i.goroutine.Do(func(ctx context.Context) {
- util.SafeCloseChan(i.ready)
- lastCompactTime := time.Now()
- ticker := time.NewTicker(DEFAULT_METRICS_INTERVAL)
- max := 0
- for {
- select {
- case <-ctx.Done():
- return
- case <-ticker.C:
- i.prefixLock.RLock()
- // report metrics
- ReportCacheMetrics(i.cacher.Name(), "index", i.prefixIndex)
- i.prefixLock.RUnlock()
- case evt, ok := <-i.prefixBuildQueue:
- if !ok {
- return
- }
- t := time.Now()
- key := util.BytesToStringWithNoCopy(evt.Object.(*mvccpb.KeyValue).Key)
- prefix := key[:strings.LastIndex(key[:len(key)-1], "/")+1]
-
- i.prefixLock.Lock()
- switch evt.Type {
- case pb.EVT_DELETE:
- i.deletePrefixKey(prefix, key)
- default:
- i.addPrefixKey(prefix, key)
- }
-
- // compact
- initSize, l := DEFAULT_CACHE_INIT_SIZE, len(i.prefixIndex)
- if max < l {
- max = l
- }
- if initSize >= l &&
- max >= initSize*DEFAULT_COMPACT_TIMES &&
- time.Now().Sub(lastCompactTime) >= DEFAULT_COMPACT_TIMEOUT {
- i.compact()
- max = l
- lastCompactTime = time.Now()
- }
-
- i.prefixLock.Unlock()
-
- util.LogNilOrWarnf(t, "too long to rebuild(action: %s) index[%d], key is %s",
- evt.Type, len(i.prefixIndex), key)
- }
- }
- util.Logger().Debugf("the goroutine building index %s is stopped", i.cacher.Name())
- })
- <-i.ready
-}
-
-func (i *Indexer) compact() {
- n := make(map[string]map[string]struct{}, DEFAULT_CACHE_INIT_SIZE)
- for k, v := range i.prefixIndex {
- c, ok := n[k]
- if !ok {
- c = make(map[string]struct{}, len(v))
- n[k] = c
- }
- for ck, cv := range v {
- c[ck] = cv
- }
- }
- i.prefixIndex = n
-
- util.Logger().Infof("index %s: compact root capacity to size %d",
- i.cacher.Name(), DEFAULT_CACHE_INIT_SIZE)
-}
-
-func (i *Indexer) getPrefixKey(arr *[]string, prefix string) (count int) {
- keysRef, ok := i.prefixIndex[prefix]
- if !ok {
- return 0
- }
-
- for key := range keysRef {
- n := i.getPrefixKey(arr, key)
- if n == 0 {
- count += len(keysRef)
- if arr != nil {
- // TODO support sort option
- for k := range keysRef {
- *arr = append(*arr, k)
- }
- }
- break
- }
- count += n
+func (i *baseIndexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (r *Response, err error) {
+ op := registry.OpGet(opts...)
+ key := util.BytesToStringWithNoCopy(op.Key)
+ if strings.Index(key, i.Cfg.Prefix) != 0 {
+ return nil, fmt.Errorf("search %s mismatch pattern %s", key, i.Cfg.Prefix)
}
- return count
-}
-func (i *Indexer) addPrefixKey(prefix, key string) {
- if i.Root == key {
- return
+ resp, err := Registry().Do(ctx, opts...)
+ if err != nil {
+ return nil, err
}
- keys, ok := i.prefixIndex[prefix]
- if !ok {
- // build parent index key and new child nodes
- keys = make(map[string]struct{})
- i.prefixIndex[prefix] = keys
- } else if _, ok := keys[key]; ok {
+ r = new(Response)
+ r.Count = resp.Count
+ if len(resp.Kvs) == 0 {
return
}
- keys[key], key = struct{}{}, prefix
- prefix = key[:strings.LastIndex(key[:len(key)-1], "/")+1]
-
- i.addPrefixKey(prefix, key)
-}
-
-func (i *Indexer) deletePrefixKey(prefix, key string) {
- m, ok := i.prefixIndex[prefix]
- if !ok {
- return
- }
- delete(m, key)
-
- // remove parent which has no child
- if len(m) == 0 {
- delete(i.prefixIndex, prefix)
- i.deletePrefixKey(prefix[:strings.LastIndex(prefix[:len(prefix)-1], "/")+1], prefix)
+ kvs := make([]*KeyValue, 0, len(resp.Kvs))
+ for _, src := range resp.Kvs {
+ kv := new(KeyValue)
+ if err = kv.From(i.Cfg.Parser, src); err != nil {
+ continue
+ }
+ kvs = append(kvs, kv)
}
+ r.Kvs = kvs
+ return
}
-func (i *Indexer) Run() {
- i.prefixLock.Lock()
- if !i.isClose {
- i.prefixLock.Unlock()
- return
- }
- i.isClose = false
- i.prefixLock.Unlock()
-
- if !core.ServerInfo.Config.EnableCache {
- util.SafeCloseChan(i.ready)
- return
- }
-
- i.buildIndex()
-
- i.cacher.Run()
+func (i *baseIndexer) Run() {
}
-func (i *Indexer) Stop() {
- i.prefixLock.Lock()
- if i.isClose {
- i.prefixLock.Unlock()
- return
- }
- i.isClose = true
- i.prefixLock.Unlock()
-
- i.cacher.Stop()
-
- i.goroutine.Close(true)
-
- close(i.prefixBuildQueue)
-
- util.SafeCloseChan(i.ready)
+func (i *baseIndexer) Stop() {
}
-func (i *Indexer) Ready() <-chan struct{} {
- <-i.cacher.Ready()
- return i.ready
+func (i *baseIndexer) Ready() <-chan struct{} {
+ return closedCh
}
-func NewCacheIndexer(root string, cr Cacher) *Indexer {
- return &Indexer{
- BuildTimeout: DEFAULT_ADD_QUEUE_TIMEOUT,
- Root: root,
- cacher: cr,
- prefixIndex: make(map[string]map[string]struct{}, DEFAULT_CACHE_INIT_SIZE),
- prefixBuildQueue: make(chan KvEvent, DEFAULT_MAX_EVENT_COUNT),
- goroutine: util.NewGo(context.Background()),
- ready: make(chan struct{}),
- isClose: true,
- }
+func NewBaseIndexer(cfg *Config) (indexer *baseIndexer) {
+ return &baseIndexer{Cfg: cfg}
}
diff --git a/server/core/backend/indexer_kv.go b/server/core/backend/indexer_kv.go
new file mode 100644
index 00000000..7cf24431
--- /dev/null
+++ b/server/core/backend/indexer_kv.go
@@ -0,0 +1,150 @@
+/*
+ * 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 backend
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "github.com/apache/incubator-servicecomb-service-center/server/core"
+ "github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+ "golang.org/x/net/context"
+ "strings"
+ "sync"
+ "time"
+)
+
+type CacheIndexer struct {
+ *baseIndexer
+
+ cacher Cacher
+ lock sync.Mutex
+ isClose bool
+}
+
+func (i *CacheIndexer) Search(ctx context.Context, opts ...registry.PluginOpOption) (*Response, error) {
+ op := registry.OpGet(opts...)
+ key := util.BytesToStringWithNoCopy(op.Key)
+ if strings.Index(key, i.baseIndexer.Cfg.Prefix) != 0 {
+ return nil, fmt.Errorf("search %s mismatch %s pattern %s",
+ key, i.cacher.Cache().Name(), i.baseIndexer.Cfg.Prefix)
+ }
+
+ if !core.ServerInfo.Config.EnableCache ||
+ op.Mode == registry.MODE_NO_CACHE ||
+ op.Revision > 0 ||
+ (op.Offset >= 0 && op.Limit > 0) {
+ util.Logger().Debugf("search %s match special options, request etcd server, opts: %s",
+ i.cacher.Cache().Name(), op)
+ return i.baseIndexer.Search(ctx, opts...)
+ }
+
+ var resp *Response
+ if op.Prefix {
+ resp = i.searchByPrefix(op)
+ } else {
+ resp = i.search(op)
+ }
+
+ if resp.Count > 0 || op.Mode == registry.MODE_CACHE {
+ return resp, nil
+ }
+
+ util.Logger().Debugf("can not find any key from %s cache with prefix, request etcd server, key: %s",
+ i.cacher.Cache().Name(), key)
+ return i.baseIndexer.Search(ctx, opts...)
+}
+
+func (i *CacheIndexer) search(op registry.PluginOp) *Response {
+ resp := new(Response)
+
+ key := util.BytesToStringWithNoCopy(op.Key)
+
+ kv := i.cacher.Cache().Get(key)
+ if kv != nil {
+ resp.Count = 1
+ }
+ if kv == nil || op.CountOnly {
+ return resp
+ }
+
+ resp.Kvs = []*KeyValue{kv}
+ return resp
+}
+
+func (i *CacheIndexer) searchByPrefix(op registry.PluginOp) *Response {
+ resp := new(Response)
+
+ prefix := util.BytesToStringWithNoCopy(op.Key)
+
+ resp.Count = int64(i.cacher.Cache().GetAll(prefix, nil))
+ if resp.Count == 0 || op.CountOnly {
+ return resp
+ }
+
+ t := time.Now()
+ kvs := make([]*KeyValue, 0, resp.Count)
+ i.cacher.Cache().GetAll(prefix, &kvs)
+
+ util.LogNilOrWarnf(t, "too long to index data[%d] from cache '%s'", len(kvs), i.cacher.Cache().Name())
+
+ resp.Kvs = kvs
+ return resp
+}
+
+func (i *CacheIndexer) Run() {
+ i.lock.Lock()
+ if !i.isClose {
+ i.lock.Unlock()
+ return
+ }
+ i.isClose = false
+ i.lock.Unlock()
+
+ i.cacher.Run()
+}
+
+func (i *CacheIndexer) Stop() {
+ i.lock.Lock()
+ if i.isClose {
+ i.lock.Unlock()
+ return
+ }
+ i.isClose = true
+ i.lock.Unlock()
+
+ i.cacher.Stop()
+}
+
+func (i *CacheIndexer) Ready() <-chan struct{} {
+ return i.cacher.Ready()
+}
+
+func NewCacheIndexer(name string, cfg *Config) (indexer *CacheIndexer) {
+ indexer = &CacheIndexer{
+ baseIndexer: NewBaseIndexer(cfg),
+ cacher: NullCacher,
+ isClose: true,
+ }
+
+ switch {
+ case core.ServerInfo.Config.EnableCache && cfg.InitSize > 0:
+ indexer.cacher = NewKvCacher(name, cfg)
+ default:
+ util.Logger().Infof("service center will not cache '%s'", name)
+ }
+ return
+}
diff --git a/server/core/backend/kv.go b/server/core/backend/kv.go
new file mode 100644
index 00000000..73f03936
--- /dev/null
+++ b/server/core/backend/kv.go
@@ -0,0 +1,40 @@
+/*
+ * 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 backend
+
+import (
+ "github.com/coreos/etcd/mvcc/mvccpb"
+)
+
+type KeyValue struct {
+ Key []byte
+ Value interface{}
+ Version int64
+ CreateRevision int64
+ ModRevision int64
+}
+
+func (kv *KeyValue) From(p *Parser, s *mvccpb.KeyValue) error {
+ kv.Key = s.Key
+ kv.Version = s.Version
+ kv.CreateRevision = s.CreateRevision
+ kv.ModRevision = s.ModRevision
+
+ v, err := p.Unmarshal(s.Value)
+ kv.Value = v
+ return err
+}
diff --git a/server/core/backend/lease.go b/server/core/backend/lease.go
index e37cafcb..89fcb2e0 100644
--- a/server/core/backend/lease.go
+++ b/server/core/backend/lease.go
@@ -56,11 +56,15 @@ func (lat *LeaseTask) Do(ctx context.Context) (err error) {
}
lat.err, err = nil, nil
- util.LogNilOrWarnf(recv, "renew lease %d(recv: %s, send: %s), key %s",
- lat.LeaseID,
- recv.Format(TIME_FORMAT),
- start.Format(TIME_FORMAT),
- lat.Key())
+
+ cost := time.Now().Sub(recv)
+ if cost >= 2*time.Second {
+ util.Logger().Warnf(nil, "[%s]renew lease %d(recv: %s, send: %s), key %s", cost,
+ lat.LeaseID,
+ recv.Format(TIME_FORMAT),
+ start.Format(TIME_FORMAT),
+ lat.Key())
+ }
return
}
diff --git a/server/core/backend/listwatch.go b/server/core/backend/listwatch.go
index 0aa46abd..469a8d14 100644
--- a/server/core/backend/listwatch.go
+++ b/server/core/backend/listwatch.go
@@ -19,9 +19,7 @@ package backend
import (
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
- "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"sync"
)
@@ -33,7 +31,7 @@ type ListWatcher struct {
rev int64
}
-func (lw *ListWatcher) List(op ListWatchConfig) ([]*mvccpb.KeyValue, error) {
+func (lw *ListWatcher) List(op ListWatchConfig) (*registry.PluginResponse, error) {
otCtx, _ := context.WithTimeout(op.Context, op.Timeout)
resp, err := lw.Client.Do(otCtx, registry.WatchPrefixOpOptions(lw.Prefix)...)
if err != nil {
@@ -41,10 +39,7 @@ func (lw *ListWatcher) List(op ListWatchConfig) ([]*mvccpb.KeyValue, error) {
return nil, err
}
lw.setRevision(resp.Revision)
- if len(resp.Kvs) == 0 {
- return nil, nil
- }
- return resp.Kvs, nil
+ return resp, nil
}
func (lw *ListWatcher) Revision() int64 {
@@ -59,7 +54,7 @@ func (lw *ListWatcher) Watch(op ListWatchConfig) *Watcher {
return newWatcher(lw, op)
}
-func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error {
+func (lw *ListWatcher) doWatch(ctx context.Context, f func(*registry.PluginResponse)) error {
rev := lw.Revision()
opts := append(
registry.WatchPrefixOpOptions(lw.Prefix),
@@ -67,29 +62,12 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
registry.WithWatchCallback(
func(message string, resp *registry.PluginResponse) error {
if resp == nil || len(resp.Kvs) == 0 {
- return fmt.Errorf("unknown event %s", resp)
+ return fmt.Errorf("unknown event %s, watch prefix %s", resp, lw.Prefix)
}
- util.Logger().Infof("caught event %s, watch prefix %s, start rev %d+1,", resp, lw.Prefix, rev)
-
lw.setRevision(resp.Revision)
- evts := make([]KvEvent, len(resp.Kvs))
- for i, kv := range resp.Kvs {
- evt := KvEvent{Prefix: lw.Prefix, Revision: kv.ModRevision}
- switch {
- case resp.Action == registry.Put && kv.Version == 1:
- evt.Type, evt.Object = proto.EVT_CREATE, kv
- case resp.Action == registry.Put:
- evt.Type, evt.Object = proto.EVT_UPDATE, kv
- case resp.Action == registry.Delete:
- evt.Type, evt.Object = proto.EVT_DELETE, kv
- default:
- return fmt.Errorf("unknown KeyValue %v", kv)
- }
- evts[i] = evt
- }
- f(evts)
+ f(resp)
return nil
}))
@@ -98,7 +76,7 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
util.Logger().Errorf(err, "watch prefix %s failed, start rev: %d+1->%d->0", lw.Prefix, rev, lw.Revision())
lw.setRevision(0)
- f([]KvEvent{errEvent(lw.Prefix, err)})
+ f(nil)
}
return err
}
@@ -106,13 +84,13 @@ func (lw *ListWatcher) doWatch(ctx context.Context, f func(evt []KvEvent)) error
type Watcher struct {
ListOps ListWatchConfig
lw *ListWatcher
- bus chan []KvEvent
+ bus chan *registry.PluginResponse
stopCh chan struct{}
stop bool
mux sync.Mutex
}
-func (w *Watcher) EventBus() <-chan []KvEvent {
+func (w *Watcher) EventBus() <-chan *registry.PluginResponse {
return w.bus
}
@@ -133,9 +111,9 @@ func (w *Watcher) process(_ context.Context) {
}
}
-func (w *Watcher) sendEvent(evts []KvEvent) {
+func (w *Watcher) sendEvent(resp *registry.PluginResponse) {
defer util.RecoverAndReport()
- w.bus <- evts
+ w.bus <- resp
}
func (w *Watcher) Stop() {
@@ -150,19 +128,11 @@ func (w *Watcher) Stop() {
w.mux.Unlock()
}
-func errEvent(key string, err error) KvEvent {
- return KvEvent{
- Type: proto.EVT_ERROR,
- Prefix: key,
- Object: err,
- }
-}
-
func newWatcher(lw *ListWatcher, listOps ListWatchConfig) *Watcher {
w := &Watcher{
ListOps: listOps,
lw: lw,
- bus: make(chan []KvEvent, EVENT_BUS_MAX_SIZE),
+ bus: make(chan *registry.PluginResponse, EVENT_BUS_MAX_SIZE),
stopCh: make(chan struct{}),
}
util.Go(w.process)
diff --git a/server/core/backend/parser.go b/server/core/backend/parser.go
new file mode 100644
index 00000000..8873988d
--- /dev/null
+++ b/server/core/backend/parser.go
@@ -0,0 +1,206 @@
+/*
+ * 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 backend
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "strings"
+)
+
+// event
+type KvEventFunc func(evt KvEvent)
+
+// new
+type CreateValueFunc func() interface{}
+
+var (
+ newBytes CreateValueFunc = func() interface{} { return []byte(nil) }
+ newString CreateValueFunc = func() interface{} { return "" }
+ newMap CreateValueFunc = func() interface{} { return make(map[string]string) }
+ newService CreateValueFunc = func() interface{} { return new(pb.MicroService) }
+ newInstance CreateValueFunc = func() interface{} { return new(pb.MicroServiceInstance) }
+ newRule CreateValueFunc = func() interface{} { return new(pb.ServiceRule) }
+ newDependencyRule CreateValueFunc = func() interface{} { return new(pb.MicroServiceDependency) }
+ newDependencyQueue CreateValueFunc = func() interface{} { return new(pb.ConsumerDependency) }
+)
+
+// parse
+type ParseValueFunc func(src []byte, dist interface{}) error
+
+var (
+ unParse ParseValueFunc = func(src []byte, dist interface{}) error {
+ d := dist.(*interface{})
+ *d = src
+ return nil
+ }
+ textUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+ d := dist.(*interface{})
+ *d = util.BytesToStringWithNoCopy(src)
+ return nil
+ }
+ mapUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+ d := dist.(*interface{})
+ m := (*d).(map[string]string)
+ return json.Unmarshal(src, &m)
+ }
+ jsonUnmarshal ParseValueFunc = func(src []byte, dist interface{}) error {
+ d := dist.(*interface{})
+ return json.Unmarshal(src, *d)
+ }
+)
+
+type Parser struct {
+ c CreateValueFunc
+ p ParseValueFunc
+}
+
+func (p *Parser) Unmarshal(src []byte) (interface{}, error) {
+ v := p.c()
+ err := p.p(src, &v)
+ return v, err
+}
+
+var (
+ BytesParser = &Parser{newBytes, unParse}
+ StringParser = &Parser{newString, textUnmarshal}
+ MapParser = &Parser{newMap, mapUnmarshal}
+ ServiceParser = &Parser{newService, jsonUnmarshal}
+ InstanceParser = &Parser{newInstance, jsonUnmarshal}
+ RuleParser = &Parser{newRule, jsonUnmarshal}
+ DependencyRuleParser = &Parser{newDependencyRule, jsonUnmarshal}
+ DependencyQueueParser = &Parser{newDependencyQueue, jsonUnmarshal}
+)
+
+func KvToResponse(kv *KeyValue) (keys []string) {
+ return strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
+}
+
+func GetInfoFromSvcKV(kv *KeyValue) (serviceId, domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 4 {
+ return
+ }
+ serviceId = keys[l-1]
+ domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
+ return
+}
+
+func GetInfoFromInstKV(kv *KeyValue) (serviceId, instanceId, domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 4 {
+ return
+ }
+ serviceId = keys[l-2]
+ instanceId = keys[l-1]
+ domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+ return
+}
+
+func GetInfoFromDomainKV(kv *KeyValue) (domain string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 1 {
+ return
+ }
+ domain = keys[l-1]
+ return
+}
+
+func GetInfoFromProjectKV(kv *KeyValue) (domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 2 {
+ return
+ }
+ domainProject = fmt.Sprintf("%s/%s", keys[l-2], keys[l-1])
+ return
+}
+
+func GetInfoFromRuleKV(kv *KeyValue) (serviceId, ruleId, domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 4 {
+ return
+ }
+ serviceId = keys[l-2]
+ ruleId = keys[l-1]
+ domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+ return
+}
+
+func GetInfoFromTagKV(kv *KeyValue) (serviceId, domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 3 {
+ return
+ }
+ serviceId = keys[l-1]
+ domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
+ return
+}
+
+func GetInfoFromSvcIndexKV(kv *KeyValue) (key *pb.MicroServiceKey) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 6 {
+ return
+ }
+ domainProject := fmt.Sprintf("%s/%s", keys[l-6], keys[l-5])
+ return &pb.MicroServiceKey{
+ Tenant: domainProject,
+ Environment: keys[l-4],
+ AppId: keys[l-3],
+ ServiceName: keys[l-2],
+ Version: keys[l-1],
+ }
+}
+
+func GetInfoFromSchemaSummaryKV(kv *KeyValue) (schemaId string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 1 {
+ return
+ }
+
+ return keys[l-1]
+}
+
+func GetInfoFromSchemaKV(kv *KeyValue) (schemaId string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 1 {
+ return
+ }
+
+ return keys[l-1]
+}
+
+func GetInfoFromDependencyQueueKV(kv *KeyValue) (consumerId, domainProject string) {
+ keys := KvToResponse(kv)
+ l := len(keys)
+ if l < 4 {
+ return
+ }
+ consumerId = keys[l-2]
+ domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
+ return
+}
diff --git a/server/core/backend/parser_test.go b/server/core/backend/parser_test.go
new file mode 100644
index 00000000..dc20af96
--- /dev/null
+++ b/server/core/backend/parser_test.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 backend
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "testing"
+)
+
+func TestParseValueFunc(t *testing.T) {
+ r, err := BytesParser.Unmarshal([]byte("a"))
+ if err != nil {
+ t.Fatalf("BytesParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.([]byte); !ok || v[0] != 'a' {
+ t.Fatalf("BytesParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = StringParser.Unmarshal([]byte("abc"))
+ if err != nil {
+ t.Fatalf("StringParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(string); !ok || v != "abc" {
+ t.Fatalf("StringParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = ServiceParser.Unmarshal([]byte(`{"serviceName": "abc"}`))
+ if err != nil {
+ t.Fatalf("ServiceParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(*proto.MicroService); !ok || v.ServiceName != "abc" {
+ t.Fatalf("ServiceParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = InstanceParser.Unmarshal([]byte(`{"hostName": "abc"}`))
+ if err != nil {
+ t.Fatalf("InstanceParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(*proto.MicroServiceInstance); !ok || v.HostName != "abc" {
+ t.Fatalf("InstanceParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = RuleParser.Unmarshal([]byte(`{"ruleId": "abc"}`))
+ if err != nil {
+ t.Fatalf("RuleParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(*proto.ServiceRule); !ok || v.RuleId != "abc" {
+ t.Fatalf("RuleParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = DependencyRuleParser.Unmarshal([]byte(`{"Dependency":[{"tenant":"opsadm/southchina","appId":"csezhq","serviceName":"zhqClient","version":"1.0.0"}]}`))
+ if err != nil {
+ t.Fatalf("DependencyRuleParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(*proto.MicroServiceDependency); !ok || v.Dependency[0].ServiceName != "zhqClient" {
+ t.Fatalf("DependencyRuleParser.Unmarshal failed, %s", v)
+ }
+
+ r, err = MapParser.Unmarshal([]byte(`{"a": "abc"}`))
+ if err != nil {
+ t.Fatalf("MapParser.Unmarshal failed, %s", err.Error())
+ }
+ if v, ok := r.(map[string]string); !ok || v["a"] != "abc" {
+ t.Fatalf("MapParser.Unmarshal failed, %s", v)
+ }
+}
diff --git a/server/core/backend/store.go b/server/core/backend/store.go
index 7a09469d..608b2d50 100644
--- a/server/core/backend/store.go
+++ b/server/core/backend/store.go
@@ -32,35 +32,43 @@ func init() {
}
type KvStore struct {
- indexers map[StoreType]*Indexer
+ indexers map[StoreType]Indexer
taskService *async.TaskService
lock sync.RWMutex
ready chan struct{}
goroutine *util.GoRoutine
isClose bool
+ rev int64
}
func (s *KvStore) Initialize() {
- s.indexers = make(map[StoreType]*Indexer)
+ s.indexers = make(map[StoreType]Indexer)
s.taskService = async.NewTaskService()
s.ready = make(chan struct{})
s.goroutine = util.NewGo(context.Background())
-
- for i := StoreType(0); i != typeEnd; i++ {
- s.newIndexBuilder(i, NullCacher)
+ for t := StoreType(0); t != typeEnd; t++ {
+ s.setupIndexer(t, NewBaseIndexer(s.injectConfig(t)))
}
- for i := StoreType(0); i != typeEnd; i++ {
- s.indexers[i].Run()
+}
+
+func (s *KvStore) OnCacheEvent(evt KvEvent) {
+ if s.rev < evt.Revision {
+ s.rev = evt.Revision
}
}
-func (s *KvStore) dispatchEvent(t StoreType, evt KvEvent) {
- s.indexers[t].OnCacheEvent(evt)
- EventProxy(t).OnEvent(evt)
+func (s *KvStore) injectConfig(t StoreType) *Config {
+ return TypeConfig[t].AppendEventFunc(s.OnCacheEvent).
+ AppendEventFunc(EventProxies[t].OnEvent)
}
-func (s *KvStore) newIndexBuilder(t StoreType, cacher Cacher) {
- s.indexers[t] = NewCacheIndexer(TypeRoots[t], cacher)
+func (s *KvStore) setupIndexer(t StoreType, indexer Indexer) {
+ old := s.indexers[t]
+ s.indexers[t] = indexer
+ indexer.Run()
+ if old != nil {
+ old.Stop()
+ }
}
func (s *KvStore) Run() {
@@ -68,25 +76,6 @@ func (s *KvStore) Run() {
s.taskService.Run()
}
-func (s *KvStore) getKvCacherCfgOptions(t StoreType) (opts []ConfigOption) {
- switch t {
- case INSTANCE:
- opts = append(opts, WithDeferHandler(s.SelfPreservationHandler()))
- }
- sz := TypeInitSize[t]
- if sz > 0 {
- opts = append(opts, WithInitSize(sz))
- }
- opts = append(opts,
- WithPrefix(TypeRoots[t]),
- WithEventFunc(func(evt KvEvent) { s.dispatchEvent(t, evt) }))
- return
-}
-
-func (s *KvStore) SelfPreservationHandler() DeferHandler {
- return &InstanceEventDeferHandler{Percent: DEFAULT_SELF_PRESERVATION_PERCENT}
-}
-
func (s *KvStore) store(ctx context.Context) {
defer s.wait(ctx)
@@ -96,17 +85,7 @@ func (s *KvStore) store(ctx context.Context) {
}
for t := StoreType(0); t != typeEnd; t++ {
- switch t {
- case SCHEMA:
- util.Logger().Infof("service center will not cache '%s'", SCHEMA)
- continue
- default:
- s.indexers[t].Stop() // release the exist indexer
- s.newIndexBuilder(t, NewKvCacher(t.String(), s.getKvCacherCfgOptions(t)...))
- }
- }
- for t := StoreType(0); t != typeEnd; t++ {
- s.indexers[t].Run()
+ s.setupIndexer(t, NewCacheIndexer(t.String(), TypeConfig[t]))
}
}
@@ -151,63 +130,63 @@ func (s *KvStore) Ready() <-chan struct{} {
return s.ready
}
-func (s *KvStore) Service() *Indexer {
+func (s *KvStore) Service() Indexer {
return s.indexers[SERVICE]
}
-func (s *KvStore) SchemaSummary() *Indexer {
+func (s *KvStore) SchemaSummary() Indexer {
return s.indexers[SCHEMA_SUMMARY]
}
-func (s *KvStore) Instance() *Indexer {
+func (s *KvStore) Instance() Indexer {
return s.indexers[INSTANCE]
}
-func (s *KvStore) Lease() *Indexer {
+func (s *KvStore) Lease() Indexer {
return s.indexers[LEASE]
}
-func (s *KvStore) ServiceIndex() *Indexer {
+func (s *KvStore) ServiceIndex() Indexer {
return s.indexers[SERVICE_INDEX]
}
-func (s *KvStore) ServiceAlias() *Indexer {
+func (s *KvStore) ServiceAlias() Indexer {
return s.indexers[SERVICE_ALIAS]
}
-func (s *KvStore) ServiceTag() *Indexer {
+func (s *KvStore) ServiceTag() Indexer {
return s.indexers[SERVICE_TAG]
}
-func (s *KvStore) Rule() *Indexer {
+func (s *KvStore) Rule() Indexer {
return s.indexers[RULE]
}
-func (s *KvStore) RuleIndex() *Indexer {
+func (s *KvStore) RuleIndex() Indexer {
return s.indexers[RULE_INDEX]
}
-func (s *KvStore) Schema() *Indexer {
+func (s *KvStore) Schema() Indexer {
return s.indexers[SCHEMA]
}
-func (s *KvStore) Dependency() *Indexer {
+func (s *KvStore) Dependency() Indexer {
return s.indexers[DEPENDENCY]
}
-func (s *KvStore) DependencyRule() *Indexer {
+func (s *KvStore) DependencyRule() Indexer {
return s.indexers[DEPENDENCY_RULE]
}
-func (s *KvStore) DependencyQueue() *Indexer {
+func (s *KvStore) DependencyQueue() Indexer {
return s.indexers[DEPENDENCY_QUEUE]
}
-func (s *KvStore) Domain() *Indexer {
+func (s *KvStore) Domain() Indexer {
return s.indexers[DOMAIN]
}
-func (s *KvStore) Project() *Indexer {
+func (s *KvStore) Project() Indexer {
return s.indexers[PROJECT]
}
@@ -234,7 +213,7 @@ func (s *KvStore) KeepAlive(ctx context.Context, opts ...registry.PluginOpOption
return pt.TTL, pt.Err()
}
-func (s *KvStore) Entity(id StoreType) *Indexer {
+func (s *KvStore) Entity(id StoreType) Indexer {
return s.indexers[id]
}
@@ -243,14 +222,9 @@ func (s *KvStore) Install(e Entity) (id StoreType, err error) {
return
}
- util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Prefix())
+ util.Logger().Infof("install new store entity %d:%s->%s", id, e.Name(), e.Config().Prefix)
- var cacher Cacher = NullCacher
- if core.ServerInfo.Config.EnableCache {
- cacher = NewKvCacher(id.String(), s.getKvCacherCfgOptions(id)...)
- }
- s.newIndexBuilder(id, cacher)
- s.indexers[id].Run()
+ s.setupIndexer(id, NewCacheIndexer(id.String(), e.Config()))
return
}
@@ -266,11 +240,6 @@ func Store() *KvStore {
return store
}
-func Revision() (rev int64) {
- for _, i := range Store().indexers {
- if rev < i.Cache().Version() {
- rev = i.Cache().Version()
- }
- }
- return
+func Revision() int64 {
+ return store.rev
}
diff --git a/server/core/key_generator.go b/server/core/key_generator.go
index 634ad68a..884e0010 100644
--- a/server/core/key_generator.go
+++ b/server/core/key_generator.go
@@ -22,6 +22,7 @@ import (
)
const (
+ SPLIT = "/"
REGISTRY_ROOT_KEY = "cse-sr"
REGISTRY_SYS_KEY = "sys"
REGISTRY_SERVICE_KEY = "ms"
@@ -44,10 +45,7 @@ const (
)
func GetRootKey() string {
- return util.StringJoin([]string{
- "",
- REGISTRY_ROOT_KEY,
- }, "/")
+ return SPLIT + REGISTRY_ROOT_KEY
}
func GetServiceRootKey(domainProject string) string {
@@ -56,7 +54,7 @@ func GetServiceRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_FILE,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceIndexRootKey(domainProject string) string {
@@ -65,7 +63,7 @@ func GetServiceIndexRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_INDEX,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceAliasRootKey(domainProject string) string {
@@ -74,7 +72,7 @@ func GetServiceAliasRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_ALIAS_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceAppKey(domainProject, env, appId string) string {
@@ -82,7 +80,7 @@ func GetServiceAppKey(domainProject, env, appId string) string {
GetServiceIndexRootKey(domainProject),
env,
appId,
- }, "/")
+ }, SPLIT)
}
func GetServiceRuleRootKey(domainProject string) string {
@@ -91,7 +89,7 @@ func GetServiceRuleRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_RULE_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceRuleIndexRootKey(domainProject string) string {
@@ -100,7 +98,7 @@ func GetServiceRuleIndexRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_RULE_INDEX_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceTagRootKey(domainProject string) string {
@@ -109,7 +107,7 @@ func GetServiceTagRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_TAG_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceSchemaRootKey(domainProject string) string {
@@ -118,7 +116,7 @@ func GetServiceSchemaRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_SCHEMA_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetInstanceRootKey(domainProject string) string {
@@ -127,7 +125,7 @@ func GetInstanceRootKey(domainProject string) string {
REGISTRY_INSTANCE_KEY,
REGISTRY_FILE,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetInstanceLeaseRootKey(domainProject string) string {
@@ -136,14 +134,14 @@ func GetInstanceLeaseRootKey(domainProject string) string {
REGISTRY_INSTANCE_KEY,
REGISTRY_LEASE_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceKey(domainProject string, serviceId string) string {
return util.StringJoin([]string{
GetServiceRootKey(domainProject),
serviceId,
- }, "/")
+ }, SPLIT)
}
func GenerateRuleIndexKey(domainProject string, serviceId string, attr string, pattern string) string {
@@ -152,7 +150,7 @@ func GenerateRuleIndexKey(domainProject string, serviceId string, attr string, p
serviceId,
attr,
pattern,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceIndexKey(key *pb.MicroServiceKey) string {
@@ -162,7 +160,7 @@ func GenerateServiceIndexKey(key *pb.MicroServiceKey) string {
key.AppId,
key.ServiceName,
key.Version,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceAliasKey(key *pb.MicroServiceKey) string {
@@ -172,7 +170,7 @@ func GenerateServiceAliasKey(key *pb.MicroServiceKey) string {
key.AppId,
key.Alias,
key.Version,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceRuleKey(domainProject string, serviceId string, ruleId string) string {
@@ -180,14 +178,14 @@ func GenerateServiceRuleKey(domainProject string, serviceId string, ruleId strin
GetServiceRuleRootKey(domainProject),
serviceId,
ruleId,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceTagKey(domainProject string, serviceId string) string {
return util.StringJoin([]string{
GetServiceTagRootKey(domainProject),
serviceId,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceSchemaKey(domainProject string, serviceId string, schemaId string) string {
@@ -195,7 +193,7 @@ func GenerateServiceSchemaKey(domainProject string, serviceId string, schemaId s
GetServiceSchemaRootKey(domainProject),
serviceId,
schemaId,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceSchemaSummaryKey(domainProject string, serviceId string, schemaId string) string {
@@ -203,7 +201,7 @@ func GenerateServiceSchemaSummaryKey(domainProject string, serviceId string, sch
GetServiceSchemaSummaryRootKey(domainProject),
serviceId,
schemaId,
- }, "/")
+ }, SPLIT)
}
func GetServiceSchemaSummaryRootKey(domainProject string) string {
@@ -212,7 +210,7 @@ func GetServiceSchemaSummaryRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_SCHEMA_SUMMARY_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GenerateInstanceKey(domainProject string, serviceId string, instanceId string) string {
@@ -220,7 +218,7 @@ func GenerateInstanceKey(domainProject string, serviceId string, instanceId stri
GetInstanceRootKey(domainProject),
serviceId,
instanceId,
- }, "/")
+ }, SPLIT)
}
func GenerateInstanceLeaseKey(domainProject string, serviceId string, instanceId string) string {
@@ -228,7 +226,7 @@ func GenerateInstanceLeaseKey(domainProject string, serviceId string, instanceId
GetInstanceLeaseRootKey(domainProject),
serviceId,
instanceId,
- }, "/")
+ }, SPLIT)
}
func GenerateServiceDependencyRuleKey(serviceType string, domainProject string, in *pb.MicroServiceKey) string {
@@ -238,7 +236,7 @@ func GenerateServiceDependencyRuleKey(serviceType string, domainProject string,
serviceType,
in.Environment,
in.ServiceName,
- }, "/")
+ }, SPLIT)
}
return util.StringJoin([]string{
GetServiceDependencyRuleRootKey(domainProject),
@@ -247,7 +245,7 @@ func GenerateServiceDependencyRuleKey(serviceType string, domainProject string,
in.AppId,
in.ServiceName,
in.Version,
- }, "/")
+ }, SPLIT)
}
func GenerateConsumerDependencyRuleKey(domainProject string, in *pb.MicroServiceKey) string {
@@ -264,7 +262,7 @@ func GetServiceDependencyRuleRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_DEPS_RULE_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetServiceDependencyQueueRootKey(domainProject string) string {
@@ -273,7 +271,7 @@ func GetServiceDependencyQueueRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_DEPS_QUEUE_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GenerateConsumerDependencyKey(domainProject string, consumerId string, providerId string) string {
@@ -286,7 +284,7 @@ func GenerateServiceDependencyKey(serviceType string, domainProject string, serv
serviceType,
serviceId1,
serviceId2,
- }, "/")
+ }, SPLIT)
}
func GenerateProviderDependencyKey(domainProject string, providerId string, consumerId string) string {
@@ -298,7 +296,7 @@ func GenerateConsumerDependencyQueueKey(domainProject, consumerId, uuid string)
GetServiceDependencyQueueRootKey(domainProject),
consumerId,
uuid,
- }, "/")
+ }, SPLIT)
}
func GetServiceDependencyRootKey(domainProject string) string {
@@ -307,35 +305,35 @@ func GetServiceDependencyRootKey(domainProject string) string {
REGISTRY_SERVICE_KEY,
REGISTRY_DEPENDENCY_KEY,
domainProject,
- }, "/")
+ }, SPLIT)
}
func GetDomainRootKey() string {
return util.StringJoin([]string{
GetRootKey(),
REGISTRY_DOMAIN_KEY,
- }, "/")
+ }, SPLIT)
}
func GenerateDomainKey(domain string) string {
return util.StringJoin([]string{
GetDomainRootKey(),
domain,
- }, "/")
+ }, SPLIT)
}
func GetServerInfoKey() string {
return util.StringJoin([]string{
GetRootKey(),
REGISTRY_SYS_KEY,
- }, "/")
+ }, SPLIT)
}
func GetMetricsRootKey() string {
return util.StringJoin([]string{
GetRootKey(),
REGISTRY_METRICS_KEY,
- }, "/")
+ }, SPLIT)
}
func GenerateMetricsKey(name, utc, domain string) string {
@@ -344,7 +342,7 @@ func GenerateMetricsKey(name, utc, domain string) string {
name,
utc,
domain,
- }, "/")
+ }, SPLIT)
}
func GetProjectRootKey(domain string) string {
@@ -352,12 +350,12 @@ func GetProjectRootKey(domain string) string {
GetRootKey(),
REGISTRY_PROJECT_KEY,
domain,
- }, "/")
+ }, SPLIT)
}
func GenerateProjectKey(domain, project string) string {
return util.StringJoin([]string{
GetProjectRootKey(domain),
project,
- }, "/")
+ }, SPLIT)
}
diff --git a/server/core/proto/services.go b/server/core/proto/services.go
index 133eb090..50f61e37 100644
--- a/server/core/proto/services.go
+++ b/server/core/proto/services.go
@@ -17,13 +17,9 @@
package proto
import (
- "fmt"
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
- "github.com/coreos/etcd/mvcc/mvccpb"
"github.com/gorilla/websocket"
"golang.org/x/net/context"
- "strings"
)
type EventType string
@@ -58,8 +54,8 @@ const (
ENV_ACCEPT string = "acceptance"
ENV_PROD string = "production"
- REGISTERBY_SDK string = "SDK"
- REGISTERBY_SIDECAR string = "SIDECAR"
+ REGISTERBY_SDK string = "SDK"
+ REGISTERBY_SIDECAR string = "SIDECAR"
REGISTERBY_PLATFORM string = "PLATFORM"
APP_ID = "default"
@@ -142,125 +138,6 @@ func CreateResponseWithSCErr(err *scerr.Error) *Response {
}
}
-func KvToResponse(kv *mvccpb.KeyValue) (keys []string, data []byte) {
- keys = strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
- data = kv.Value
- return
-}
-
-func GetInfoFromSvcKV(kv *mvccpb.KeyValue) (serviceId, domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 4 {
- return
- }
- serviceId = keys[l-1]
- domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
- return
-}
-
-func GetInfoFromInstKV(kv *mvccpb.KeyValue) (serviceId, instanceId, domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 4 {
- return
- }
- serviceId = keys[l-2]
- instanceId = keys[l-1]
- domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
- return
-}
-
-func GetInfoFromDomainKV(kv *mvccpb.KeyValue) (domain string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 1 {
- return
- }
- domain = keys[l-1]
- return
-}
-
-func GetInfoFromProjectKV(kv *mvccpb.KeyValue) (domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 2 {
- return
- }
- domainProject = fmt.Sprintf("%s/%s", keys[l-2], keys[l-1])
- return
-}
-
-func GetInfoFromRuleKV(kv *mvccpb.KeyValue) (serviceId, ruleId, domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 4 {
- return
- }
- serviceId = keys[l-2]
- ruleId = keys[l-1]
- domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
- return
-}
-
-func GetInfoFromTagKV(kv *mvccpb.KeyValue) (serviceId, domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 3 {
- return
- }
- serviceId = keys[l-1]
- domainProject = fmt.Sprintf("%s/%s", keys[l-3], keys[l-2])
- return
-}
-
-func GetInfoFromSvcIndexKV(kv *mvccpb.KeyValue) (key *MicroServiceKey, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 6 {
- return
- }
- domainProject := fmt.Sprintf("%s/%s", keys[l-6], keys[l-5])
- return &MicroServiceKey{
- Tenant: domainProject,
- Environment: keys[l-4],
- AppId: keys[l-3],
- ServiceName: keys[l-2],
- Version: keys[l-1],
- }, data
-}
-
-func GetInfoFromSchemaSummaryKV(kv *mvccpb.KeyValue) (schemaId string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 1 {
- return
- }
-
- return keys[l-1], data
-}
-
-func GetInfoFromSchemaKV(kv *mvccpb.KeyValue) (schemaId string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 1 {
- return
- }
-
- return keys[l-1], data
-}
-
-func GetInfoFromDependencyQueueKV(kv *mvccpb.KeyValue) (consumerId, domainProject string, data []byte) {
- keys, data := KvToResponse(kv)
- l := len(keys)
- if l < 4 {
- return
- }
- consumerId = keys[l-2]
- domainProject = fmt.Sprintf("%s/%s", keys[l-4], keys[l-3])
- return
-}
-
func DependenciesToKeys(in []*MicroServiceKey, domainProject string) []*MicroServiceKey {
for _, value := range in {
if len(value.Tenant) == 0 {
diff --git a/server/govern/service.go b/server/govern/service.go
index e0c695f6..139fe7bc 100644
--- a/server/govern/service.go
+++ b/server/govern/service.go
@@ -206,7 +206,7 @@ func (governService *GovernService) GetApplications(ctx context.Context, in *pb.
apps := make([]string, 0, l)
appMap := make(map[string]struct{}, l)
for _, kv := range resp.Kvs {
- key, _ := pb.GetInfoFromSvcIndexKV(kv)
+ key := backend.GetInfoFromSvcIndexKV(kv)
if _, ok := appMap[key.AppId]; ok {
continue
}
@@ -236,7 +236,7 @@ func getServiceAllVersions(ctx context.Context, serviceKey *pb.MicroServiceKey)
return versions, nil
}
for _, kv := range resp.Kvs {
- key, _ := pb.GetInfoFromSvcIndexKV(kv)
+ key := backend.GetInfoFromSvcIndexKV(kv)
versions = append(versions, key.Version)
}
return versions, nil
@@ -255,7 +255,7 @@ func getSchemaInfoUtil(ctx context.Context, domainProject string, serviceId stri
schemas := make([]*pb.Schema, 0, len(resp.Kvs))
for _, kv := range resp.Kvs {
schemaInfo := &pb.Schema{}
- schemaInfo.Schema = util.BytesToStringWithNoCopy(kv.Value)
+ schemaInfo.Schema = util.BytesToStringWithNoCopy(kv.Value.([]byte))
schemaInfo.SchemaId = util.BytesToStringWithNoCopy(kv.Key[len(key):])
schemas = append(schemas, schemaInfo)
}
@@ -367,7 +367,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
svcWithNonVersion := make(map[string]struct{}, respSvc.Count)
svcIdToNonVerKey := make(map[string]string, respSvc.Count)
for _, kv := range respSvc.Kvs {
- key, val := pb.GetInfoFromSvcIndexKV(kv)
+ key := backend.GetInfoFromSvcIndexKV(kv)
if _, ok := app[key.AppId]; !ok {
app[key.AppId] = struct{}{}
}
@@ -377,7 +377,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
if _, ok := svcWithNonVersion[svcWithNonVersionKey]; !ok {
svcWithNonVersion[svcWithNonVersionKey] = struct{}{}
}
- svcIdToNonVerKey[util.BytesToStringWithNoCopy(val)] = svcWithNonVersionKey
+ svcIdToNonVerKey[kv.Value.(string)] = svcWithNonVersionKey
}
result.Services.Count = int64(len(svcWithNonVersion))
@@ -401,7 +401,7 @@ func statistics(ctx context.Context) (*pb.Statistics, error) {
onlineServices := make(map[string]struct{}, respSvc.Count)
for _, kv := range respIns.Kvs {
- serviceId, _, _, _ := pb.GetInfoFromInstKV(kv)
+ serviceId, _, _ := backend.GetInfoFromInstKV(kv)
key, ok := svcIdToNonVerKey[serviceId]
if !ok {
continue
diff --git a/server/plugin/infra/quota/buildin/buildin.go b/server/plugin/infra/quota/buildin/buildin.go
index 6dffeac1..6c90e756 100644
--- a/server/plugin/infra/quota/buildin/buildin.go
+++ b/server/plugin/infra/quota/buildin/buildin.go
@@ -31,7 +31,7 @@ import (
)
const (
- SERVICE_NUM_MAX_LIMIT = 12000
+ SERVICE_NUM_MAX_LIMIT = 50000
INSTANCE_NUM_MAX_LIMIT = 150000
RULE_NUM_MAX_LIMIT_PER_SERVICE = 100
SCHEMA_NUM_MAX_LIMIT_PER_SERVICE = 100
@@ -86,7 +86,7 @@ func (q *BuildInQuota) RemandQuotas(ctx context.Context, quotaType quota.Resourc
func ResourceLimitHandler(ctx context.Context, res *quota.ApplyQuotaResource) *quota.ApplyQuotaResult {
var key string
var max int64 = 0
- var indexer *backend.Indexer
+ var indexer backend.Indexer
domainProject := res.DomainProject
serviceId := res.ServiceId
diff --git a/server/plugin/infra/registry/etcd/etcd.go b/server/plugin/infra/registry/etcd/etcd.go
index 6a4d5db6..db9bc898 100644
--- a/server/plugin/infra/registry/etcd/etcd.go
+++ b/server/plugin/infra/registry/etcd/etcd.go
@@ -462,7 +462,8 @@ func (c *EtcdClient) TxnWithCmp(ctx context.Context, success []registry.PluginOp
if err != nil {
return nil, err
}
- util.LogNilOrWarnf(start, "registry client txn {if: %s, then: %d, else: %d}", cmps, len(success), len(fail))
+ util.LogNilOrWarnf(start, "registry client txn {if(%v): %s, then: %d, else: %d}, rev: %d",
+ resp.Succeeded, cmps, len(success), len(fail), resp.Header.Revision)
return ®istry.PluginResponse{
Succeeded: resp.Succeeded,
Revision: resp.Header.Revision,
diff --git a/server/server.go b/server/server.go
index 7e98b6a1..66de1090 100644
--- a/server/server.go
+++ b/server/server.go
@@ -180,11 +180,6 @@ func (s *ServiceCenterServer) autoCompactBackend() {
}
func (s *ServiceCenterServer) startNotifyService() {
- s.notifyService.Config = nf.NotifyServiceConfig{
- AddTimeout: 30 * time.Second,
- NotifyTimeout: 30 * time.Second,
- MaxQueue: 100,
- }
s.notifyService.Start()
}
diff --git a/server/service/cache/common.go b/server/service/cache/common.go
new file mode 100644
index 00000000..b17da58e
--- /dev/null
+++ b/server/service/cache/common.go
@@ -0,0 +1,26 @@
+/*
+ * 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 cache
+
+const (
+ CTX_FIND_CONSUMER = "consumer"
+ CTX_FIND_PROVIDER = "provider"
+ CTX_FIND_TAGS = "tags"
+
+ CACHE_FIND = "find"
+ CACHE_DEP = "dep"
+)
diff --git a/server/service/cache/dependency.go b/server/service/cache/dependency.go
new file mode 100644
index 00000000..23dd2626
--- /dev/null
+++ b/server/service/cache/dependency.go
@@ -0,0 +1,64 @@
+/*
+ * 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 cache
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+ "math"
+)
+
+var DependencyRule = &DependencyRuleCache{
+ Tree: cache.NewTree(cache.Configure().
+ WithMaxSize(math.MaxInt64))}
+
+func init() {
+ DependencyRule.AddFilter(
+ &ServiceFilter{},
+ &ConsumerFilter{})
+}
+
+type DependencyRuleItem struct {
+ VersionRule string
+}
+
+type DependencyRuleCache struct {
+ *cache.Tree
+}
+
+func (f *DependencyRuleCache) ExistVersionRule(ctx context.Context, consumerId string, provider *pb.MicroServiceKey) bool {
+ cloneCtx := context.WithValue(context.WithValue(ctx,
+ CTX_FIND_CONSUMER, consumerId),
+ CTX_FIND_PROVIDER, provider)
+
+ node, _ := f.Tree.Get(cloneCtx, cache.Options().Temporary(ctx.Value(serviceUtil.CTX_NOCACHE) == "1"))
+ if node == nil {
+ return false
+ }
+ v := node.Cache.Get(CACHE_DEP).(*DependencyRuleItem)
+ if v.VersionRule != provider.Version {
+ v.VersionRule = provider.Version
+ return false
+ }
+ return true
+}
+
+func (f *DependencyRuleCache) Remove(provider *pb.MicroServiceKey) {
+ f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, provider))
+}
diff --git a/server/service/cache/filter_consumer.go b/server/service/cache/filter_consumer.go
new file mode 100644
index 00000000..25f29cee
--- /dev/null
+++ b/server/service/cache/filter_consumer.go
@@ -0,0 +1,35 @@
+/*
+ * 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 cache
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "golang.org/x/net/context"
+)
+
+type ConsumerFilter struct {
+}
+
+func (f *ConsumerFilter) Name(ctx context.Context) string {
+ return ctx.Value(CTX_FIND_CONSUMER).(string)
+}
+
+func (f *ConsumerFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ node = cache.NewNode()
+ node.Cache.Set(CACHE_DEP, &DependencyRuleItem{})
+ return
+}
diff --git a/server/service/cache/filter_instances.go b/server/service/cache/filter_instances.go
new file mode 100644
index 00000000..1c386762
--- /dev/null
+++ b/server/service/cache/filter_instances.go
@@ -0,0 +1,72 @@
+/*
+ * 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 cache
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ apt "github.com/apache/incubator-servicecomb-service-center/server/core"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+)
+
+type InstancesFilter struct {
+}
+
+func (f *InstancesFilter) Name(ctx context.Context) string {
+ return ""
+}
+
+func (f *InstancesFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+
+ var (
+ instances []*pb.MicroServiceInstance
+ rev int64
+ )
+ for _, providerServiceId := range pCopy.ServiceIds {
+ key := apt.GenerateInstanceKey(provider.Tenant, providerServiceId, "")
+ opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key), registry.WithPrefix())
+ resp, err := backend.Store().Instance().Search(ctx, opts...)
+ if err != nil {
+ consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+ findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+ provider.AppId, provider.ServiceName, provider.Version)
+ util.Logger().Errorf(err, "Instance().Search failed, %s", findFlag)
+ return nil, err
+ }
+
+ for _, kv := range resp.Kvs {
+ if kv.ModRevision > rev {
+ rev = kv.ModRevision
+ }
+ instances = append(instances, kv.Value.(*pb.MicroServiceInstance))
+ }
+ }
+
+ pCopy.Instances = instances
+ pCopy.Rev = serviceUtil.FormatRevision(rev, int64(len(instances)))
+
+ node = cache.NewNode()
+ node.Cache.Set(CACHE_FIND, &pCopy)
+ return
+}
diff --git a/server/service/cache/filter_permission.go b/server/service/cache/filter_permission.go
new file mode 100644
index 00000000..69e4dd8d
--- /dev/null
+++ b/server/service/cache/filter_permission.go
@@ -0,0 +1,60 @@
+/*
+ * 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 cache
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+)
+
+type AccessibleFilter struct {
+}
+
+func (f *AccessibleFilter) Name(ctx context.Context) string {
+ consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+ return consumer.ServiceId
+}
+
+func (f *AccessibleFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ var ids []string
+ consumerId := f.Name(ctx)
+ pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+ for _, providerServiceId := range pCopy.ServiceIds {
+ if err := serviceUtil.Accessible(ctx, consumerId, providerServiceId); err != nil {
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumerId,
+ provider.AppId, provider.ServiceName, provider.Version)
+ util.Logger().Errorf(err, "AccessibleFilter failed, %s", findFlag)
+ continue
+ }
+ ids = append(ids, providerServiceId)
+ }
+
+ if len(ids) == 0 {
+ return
+ }
+
+ pCopy.ServiceIds = ids
+
+ node = cache.NewNode()
+ node.Cache.Set(CACHE_FIND, &pCopy)
+ return
+}
diff --git a/server/service/cache/filter_service.go b/server/service/cache/filter_service.go
new file mode 100644
index 00000000..37bee6f6
--- /dev/null
+++ b/server/service/cache/filter_service.go
@@ -0,0 +1,41 @@
+/*
+ * 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 cache
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "golang.org/x/net/context"
+)
+
+type ServiceFilter struct {
+}
+
+func (f *ServiceFilter) Name(ctx context.Context) string {
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ return util.StringJoin([]string{
+ provider.Tenant,
+ provider.Environment,
+ provider.AppId,
+ provider.ServiceName}, "/")
+}
+
+func (f *ServiceFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ node = cache.NewNode()
+ return
+}
diff --git a/server/service/cache/filter_tags.go b/server/service/cache/filter_tags.go
new file mode 100644
index 00000000..1964f157
--- /dev/null
+++ b/server/service/cache/filter_tags.go
@@ -0,0 +1,83 @@
+/*
+ * 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 cache
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+ "sort"
+ "strings"
+)
+
+type TagsFilter struct {
+}
+
+func (f *TagsFilter) Name(ctx context.Context) string {
+ tags, _ := ctx.Value(CTX_FIND_TAGS).([]string)
+ sort.Strings(tags)
+ return strings.Join(tags, ",")
+}
+
+func (f *TagsFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ tags, _ := ctx.Value(CTX_FIND_TAGS).([]string)
+ if len(tags) == 0 {
+ node = cache.NewNode()
+ node.Cache = parent.Cache
+ return
+ }
+
+ var ids []string
+
+ targetDomainProject := util.ParseTargetDomainProject(ctx)
+ pCopy := *parent.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem)
+
+loopProviderIds:
+ for _, providerServiceId := range pCopy.ServiceIds {
+ tagsFromETCD, err := serviceUtil.GetTagsUtils(ctx, targetDomainProject, providerServiceId)
+ if err != nil {
+ consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+ provider.AppId, provider.ServiceName, provider.Version)
+ util.Logger().Errorf(err, "TagsFilter failed, %s", findFlag)
+ return nil, err
+ }
+ if len(tagsFromETCD) == 0 {
+ continue
+ }
+ for _, tag := range tags {
+ if _, ok := tagsFromETCD[tag]; !ok {
+ continue loopProviderIds
+ }
+ }
+ ids = append(ids, providerServiceId)
+ }
+
+ if len(ids) == 0 {
+ return
+ }
+
+ pCopy.ServiceIds = ids
+
+ node = cache.NewNode()
+ node.Cache.Set(CACHE_FIND, &pCopy)
+ return
+}
diff --git a/server/service/cache/filter_version.go b/server/service/cache/filter_version.go
new file mode 100644
index 00000000..f5bb708b
--- /dev/null
+++ b/server/service/cache/filter_version.go
@@ -0,0 +1,57 @@
+/*
+ * 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 cache
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+)
+
+type VersionRuleFilter struct {
+}
+
+func (f *VersionRuleFilter) Name(ctx context.Context) string {
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ return provider.Version
+}
+
+func (f *VersionRuleFilter) Init(ctx context.Context, parent *cache.Node) (node *cache.Node, err error) {
+ provider := ctx.Value(CTX_FIND_PROVIDER).(*pb.MicroServiceKey)
+ // 版本规则
+ ids, err := serviceUtil.FindServiceIds(ctx, provider.Version, provider)
+ if err != nil {
+ consumer := ctx.Value(CTX_FIND_CONSUMER).(*pb.MicroService)
+ findFlag := fmt.Sprintf("consumer %s find provider %s/%s/%s", consumer.ServiceId,
+ provider.AppId, provider.ServiceName, provider.Version)
+ util.Logger().Errorf(err, "VersionRuleFilter failed, %s", findFlag)
+ return
+ }
+ if len(ids) == 0 {
+ return
+ }
+
+ node = cache.NewNode()
+ node.Cache.Set(CACHE_FIND, &VersionRuleCacheItem{
+ VersionRule: provider.Version,
+ ServiceIds: ids,
+ })
+ return
+}
diff --git a/server/service/cache/instance.go b/server/service/cache/instance.go
new file mode 100644
index 00000000..52967f7f
--- /dev/null
+++ b/server/service/cache/instance.go
@@ -0,0 +1,73 @@
+/*
+ * 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 cache
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/cache"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
+ "golang.org/x/net/context"
+ "math"
+ "time"
+)
+
+var FindInstances = &FindInstancesCache{
+ Tree: cache.NewTree(cache.Configure().
+ WithTTL(2 * time.Minute).
+ WithMaxSize(math.MaxInt64))}
+
+func init() {
+ FindInstances.AddFilter(
+ &ServiceFilter{},
+ &VersionRuleFilter{},
+ &TagsFilter{},
+ &AccessibleFilter{},
+ &InstancesFilter{})
+}
+
+type VersionRuleCacheItem struct {
+ VersionRule string
+ ServiceIds []string
+ Instances []*pb.MicroServiceInstance
+ Rev string
+}
+
+type FindInstancesCache struct {
+ *cache.Tree
+}
+
+func (f *FindInstancesCache) Get(ctx context.Context, consumer *pb.MicroService, provider *pb.MicroServiceKey, tags []string) (*VersionRuleCacheItem, error) {
+ cloneCtx := context.WithValue(context.WithValue(context.WithValue(ctx,
+ CTX_FIND_CONSUMER, consumer),
+ CTX_FIND_PROVIDER, provider),
+ CTX_FIND_TAGS, tags)
+
+ node, err := f.Tree.Get(cloneCtx, cache.Options().Temporary(ctx.Value(serviceUtil.CTX_NOCACHE) == "1"))
+ if node == nil {
+ return nil, err
+ }
+ return node.Cache.Get(CACHE_FIND).(*VersionRuleCacheItem), nil
+}
+
+func (f *FindInstancesCache) Remove(provider *pb.MicroServiceKey) {
+ f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, provider))
+ if len(provider.Alias) > 0 {
+ copy := *provider
+ copy.ServiceName = copy.Alias
+ f.Tree.Remove(context.WithValue(context.Background(), CTX_FIND_PROVIDER, ©))
+ }
+}
diff --git a/server/service/event/dependency_event_handler.go b/server/service/event/dependency_event_handler.go
index 2bcbf1df..235fa028 100644
--- a/server/service/event/dependency_event_handler.go
+++ b/server/service/event/dependency_event_handler.go
@@ -17,7 +17,6 @@
package event
import (
- "encoding/json"
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
"github.com/apache/incubator-servicecomb-service-center/server/core"
@@ -26,7 +25,6 @@ import (
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
"github.com/apache/incubator-servicecomb-service-center/server/mux"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"time"
)
@@ -90,11 +88,11 @@ func (h *DependencyEventHandler) loop() {
type DependencyEventHandlerResource struct {
dep *pb.ConsumerDependency
- kv *mvccpb.KeyValue
+ kv *backend.KeyValue
domainProject string
}
-func NewDependencyEventHandlerResource(dep *pb.ConsumerDependency, kv *mvccpb.KeyValue, domainProject string) *DependencyEventHandlerResource {
+func NewDependencyEventHandlerResource(dep *pb.ConsumerDependency, kv *backend.KeyValue, domainProject string) *DependencyEventHandlerResource {
return &DependencyEventHandlerResource{
dep,
kv,
@@ -126,25 +124,12 @@ func (h *DependencyEventHandler) Handle() error {
return nil
}
- ctx := context.Background()
-
dependencyTree := util.NewTree(isAddToLeft)
for _, kv := range resp.Kvs {
- r := &pb.ConsumerDependency{}
- consumerId, domainProject, data := pb.GetInfoFromDependencyQueueKV(kv)
-
- err := json.Unmarshal(data, r)
- if err != nil {
- util.Logger().Errorf(err, "maintain dependency failed, unmarshal failed, consumer %s dependency: %s",
- consumerId, util.BytesToStringWithNoCopy(data))
-
- if err = h.removeKV(ctx, kv); err != nil {
- return err
- }
- continue
- }
+ r := kv.Value.(*pb.ConsumerDependency)
+ _, domainProject := backend.GetInfoFromDependencyQueueKV(kv)
res := NewDependencyEventHandlerResource(r, kv, domainProject)
dependencyTree.AddNode(res)
@@ -188,7 +173,7 @@ func (h *DependencyEventHandler) dependencyRuleHandle(res interface{}) error {
return nil
}
-func (h *DependencyEventHandler) removeKV(ctx context.Context, kv *mvccpb.KeyValue) error {
+func (h *DependencyEventHandler) removeKV(ctx context.Context, kv *backend.KeyValue) error {
dResp, err := backend.Registry().TxnWithCmp(ctx, []registry.PluginOp{registry.OpDel(registry.WithKey(kv.Key))},
[]registry.CompareOp{registry.OpCmp(registry.CmpVer(kv.Key), registry.CMP_EQUAL, kv.Version)},
nil)
diff --git a/server/service/event/instance_event_handler.go b/server/service/event/instance_event_handler.go
index a80376d1..bd7e8eb7 100644
--- a/server/service/event/instance_event_handler.go
+++ b/server/service/event/instance_event_handler.go
@@ -17,14 +17,13 @@
package event
import (
- "encoding/json"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
apt "github.com/apache/incubator-servicecomb-service-center/server/core"
"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/service/cache"
nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"strings"
)
@@ -42,33 +41,26 @@ func (h *InstanceEventHandler) OnEvent(evt backend.KvEvent) {
return
}
- kv := evt.Object.(*mvccpb.KeyValue)
- providerId, providerInstanceId, domainProject, data := pb.GetInfoFromInstKV(kv)
- if data == nil {
- util.Logger().Errorf(nil,
- "unmarshal provider service instance file failed, instance %s/%s [%s] event, data is nil",
- providerId, providerInstanceId, action)
- return
- }
+ providerId, providerInstanceId, domainProject := backend.GetInfoFromInstKV(evt.KV)
if action == pb.EVT_DELETE {
splited := strings.Split(domainProject, "/")
if len(splited) == 2 && !apt.IsDefaultDomainProject(domainProject) {
- domainName := splited[0]
- projectName := splited[1]
- ctx := util.SetDomainProject(context.Background(), domainName, projectName)
- serviceUtil.RemandInstanceQuota(ctx)
+ domainName, projectName := splited[0], splited[1]
+ serviceUtil.RemandInstanceQuota(
+ util.SetDomainProject(context.Background(), domainName, projectName))
}
}
if nf.GetNotifyService().Closed() {
- util.Logger().Warnf(nil, "caught instance %s/%s [%s] event, but notify service is closed",
- providerId, providerInstanceId, action)
+ util.Logger().Warnf(nil, "caught [%s] instance event %s/%s, but notify service is closed",
+ action, providerId, providerInstanceId)
return
}
- util.Logger().Infof("caught instance %s/%s [%s] event", providerId, providerInstanceId, action)
+ util.Logger().Infof("caught [%s] instance event %s/%s", action, providerId, providerInstanceId)
// 查询服务版本信息
- ms, err := serviceUtil.GetServiceInCache(context.Background(), domainProject, providerId)
+ ctx := util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1")
+ ms, err := serviceUtil.GetService(ctx, domainProject, providerId)
if ms == nil {
util.Logger().Warnf(err, "get provider service %s/%s id in cache failed",
providerId, providerInstanceId)
@@ -76,29 +68,14 @@ func (h *InstanceEventHandler) OnEvent(evt backend.KvEvent) {
}
// 查询所有consumer
- consumerIds, _, err := serviceUtil.GetConsumerIdsByProvider(context.Background(), domainProject, ms)
+ consumerIds, _, err := serviceUtil.GetAllConsumerIds(ctx, domainProject, ms)
if err != nil {
util.Logger().Errorf(err, "query service %s consumers failed", providerId)
return
}
- if len(consumerIds) == 0 {
- return
- }
-
- var instance pb.MicroServiceInstance
- err = json.Unmarshal(data, &instance)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal provider service instance %s/%s file failed",
- providerId, providerInstanceId)
- return
- }
- PublishInstanceEvent(domainProject, action, &pb.MicroServiceKey{
- Environment: ms.Environment,
- AppId: ms.AppId,
- ServiceName: ms.ServiceName,
- Version: ms.Version,
- }, &instance, evt.Revision, consumerIds)
+ PublishInstanceEvent(domainProject, action, pb.MicroServiceToKey(domainProject, ms),
+ evt.KV.Value.(*pb.MicroServiceInstance), evt.Revision, consumerIds)
}
func NewInstanceEventHandler() *InstanceEventHandler {
@@ -106,6 +83,12 @@ func NewInstanceEventHandler() *InstanceEventHandler {
}
func PublishInstanceEvent(domainProject string, action pb.EventType, serviceKey *pb.MicroServiceKey, instance *pb.MicroServiceInstance, rev int64, subscribers []string) {
+ defer cache.FindInstances.Remove(serviceKey)
+
+ if len(subscribers) == 0 {
+ return
+ }
+
response := &pb.WatchInstanceResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Watch instance successfully."),
Action: string(action),
@@ -113,9 +96,6 @@ func PublishInstanceEvent(domainProject string, action pb.EventType, serviceKey
Instance: instance,
}
for _, consumerId := range subscribers {
- // expires cache
- serviceUtil.FindInstancesCache.Delete(domainProject, consumerId, serviceKey)
-
// TODO add超时怎么处理?
job := nf.NewWatchJob(consumerId, apt.GetInstanceRootKey(domainProject)+"/", rev, response)
nf.GetNotifyService().AddJob(job)
diff --git a/server/service/event/rule_event_handler.go b/server/service/event/rule_event_handler.go
index 474811e2..4e6cf925 100644
--- a/server/service/event/rule_event_handler.go
+++ b/server/service/event/rule_event_handler.go
@@ -17,7 +17,6 @@
package event
import (
- "encoding/json"
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/async"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
@@ -25,7 +24,6 @@ import (
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
)
@@ -52,7 +50,9 @@ func (apt *RulesChangedTask) Err() error {
}
func (apt *RulesChangedTask) publish(ctx context.Context, domainProject, providerId string, rev int64) error {
- provider, err := serviceUtil.GetServiceInCache(ctx, domainProject, providerId)
+ ctx = util.SetContext(ctx, serviceUtil.CTX_CACHEONLY, "1")
+
+ provider, err := serviceUtil.GetService(ctx, domainProject, providerId)
if err != nil {
util.Logger().Errorf(err, "get provider %s service file failed", providerId)
return err
@@ -62,7 +62,7 @@ func (apt *RulesChangedTask) publish(ctx context.Context, domainProject, provide
return fmt.Errorf("provider %s does not exist", providerId)
}
- consumerIds, err := serviceUtil.GetConsumersInCache(ctx, domainProject, provider)
+ consumerIds, err := serviceUtil.GetConsumerIds(ctx, domainProject, provider)
if err != nil {
util.Logger().Errorf(err, "get consumer services by provider %s failed", providerId)
return err
@@ -86,29 +86,13 @@ func (h *RuleEventHandler) OnEvent(evt backend.KvEvent) {
return
}
- kv := evt.Object.(*mvccpb.KeyValue)
- providerId, ruleId, domainProject, data := pb.GetInfoFromRuleKV(kv)
- if data == nil {
- util.Logger().Errorf(nil,
- "unmarshal service rule file failed, service %s rule %s [%s] event, data is nil",
- providerId, ruleId, action)
- return
- }
-
+ providerId, ruleId, domainProject := backend.GetInfoFromRuleKV(evt.KV)
if nf.GetNotifyService().Closed() {
- util.Logger().Warnf(nil, "caught service %s rule %s [%s] event, but notify service is closed",
- providerId, ruleId, action)
- return
- }
- util.Logger().Infof("caught service %s rule %s [%s] event", providerId, ruleId, action)
-
- var rule pb.ServiceRule
- err := json.Unmarshal(data, &rule)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal service %s rule %s file failed",
- providerId, ruleId)
+ util.Logger().Warnf(nil, "caught [%s] service rule event %s/%s, but notify service is closed",
+ action, providerId, ruleId)
return
}
+ util.Logger().Infof("caught [%s] service rule event %s/%s", action, providerId, ruleId)
async.Service().Add(context.Background(),
NewRulesChangedAsyncTask(domainProject, providerId, evt.Revision))
diff --git a/server/service/event/service_event_handler.go b/server/service/event/service_event_handler.go
index 45392f17..dc17730c 100644
--- a/server/service/event/service_event_handler.go
+++ b/server/service/event/service_event_handler.go
@@ -20,8 +20,8 @@ import (
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/service/cache"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"strings"
)
@@ -34,27 +34,30 @@ func (h *ServiceEventHandler) Type() backend.StoreType {
}
func (h *ServiceEventHandler) OnEvent(evt backend.KvEvent) {
- action := evt.Type
- if action != pb.EVT_CREATE && action != pb.EVT_INIT {
- return
- }
+ ms := evt.KV.Value.(*pb.MicroService)
+ _, domainProject := backend.GetInfoFromSvcKV(evt.KV)
- kv := evt.Object.(*mvccpb.KeyValue)
- serviceId, domainProject, data := pb.GetInfoFromSvcKV(kv)
- if data == nil {
- util.Logger().Errorf(nil,
- "unmarshal service file failed, service %s [%s] event, data is nil",
- serviceId, action)
- return
+ switch evt.Type {
+ case pb.EVT_INIT, pb.EVT_CREATE:
+ newDomain := domainProject[:strings.Index(domainProject, "/")]
+ newProject := domainProject[strings.Index(domainProject, "/")+1:]
+ err := serviceUtil.NewDomainProject(context.Background(), newDomain, newProject)
+ if err != nil {
+ util.Logger().Errorf(err, "new domain(%s) or project(%s) failed", newDomain, newProject)
+ }
+ default:
}
- newDomain := domainProject[:strings.Index(domainProject, "/")]
- newProject := domainProject[strings.Index(domainProject, "/")+1:]
- err := serviceUtil.NewDomainProject(context.Background(), newDomain, newProject)
- if err != nil {
- util.Logger().Errorf(err, "new domain(%s) or project(%s) failed", newDomain, newProject)
+ if evt.Type == pb.EVT_INIT {
return
}
+
+ util.Logger().Infof("caught [%s] service %s/%s/%s event",
+ evt.Type, ms.AppId, ms.ServiceName, ms.Version)
+
+ // cache
+ providerKey := pb.MicroServiceToKey(domainProject, ms)
+ cache.FindInstances.Remove(providerKey)
}
func NewServiceEventHandler() *ServiceEventHandler {
diff --git a/server/service/event/tag_event_handler.go b/server/service/event/tag_event_handler.go
index a83d2c5b..fe09ad8d 100644
--- a/server/service/event/tag_event_handler.go
+++ b/server/service/event/tag_event_handler.go
@@ -17,15 +17,14 @@
package event
import (
- "encoding/json"
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/async"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/service/cache"
nf "github.com/apache/incubator-servicecomb-service-center/server/service/notification"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
)
@@ -52,34 +51,36 @@ func (apt *TagsChangedTask) Err() error {
}
func (apt *TagsChangedTask) publish(ctx context.Context, domainProject, consumerId string, rev int64) error {
- consumer, err := serviceUtil.GetServiceInCache(ctx, domainProject, consumerId)
+ ctx = util.SetContext(ctx, serviceUtil.CTX_CACHEONLY, "1")
+
+ consumer, err := serviceUtil.GetService(ctx, domainProject, consumerId)
if err != nil {
- util.Logger().Errorf(err, "get comsumer for publish event %s failed", consumerId)
+ util.Logger().Errorf(err, "get consumer for publish event %s failed", consumerId)
return err
}
if consumer == nil {
util.Logger().Errorf(nil, "service not exist, %s", consumerId)
return fmt.Errorf("service not exist, %s", consumerId)
}
- providerIds, err := serviceUtil.GetProvidersInCache(ctx, domainProject, consumer)
+
+ serviceKey := pb.MicroServiceToKey(domainProject, consumer)
+ cache.FindInstances.Remove(serviceKey)
+
+ providerIds, err := serviceUtil.GetProviderIds(ctx, domainProject, consumer)
if err != nil {
util.Logger().Errorf(err, "get provider services by consumer %s failed", consumerId)
return err
}
for _, providerId := range providerIds {
- provider, err := serviceUtil.GetServiceInCache(ctx, domainProject, providerId)
+ provider, err := serviceUtil.GetService(ctx, domainProject, providerId)
if provider == nil {
util.Logger().Warnf(err, "get service %s file failed", providerId)
continue
}
- PublishInstanceEvent(domainProject, pb.EVT_EXPIRE,
- &pb.MicroServiceKey{
- Environment: provider.Environment,
- AppId: provider.AppId,
- ServiceName: provider.ServiceName,
- Version: provider.Version,
- }, nil, rev, []string{consumerId})
+ providerKey := pb.MicroServiceToKey(domainProject, provider)
+
+ PublishInstanceEvent(domainProject, pb.EVT_EXPIRE, providerKey, nil, rev, []string{consumerId})
}
return nil
}
@@ -97,28 +98,14 @@ func (h *TagEventHandler) OnEvent(evt backend.KvEvent) {
return
}
- kv := evt.Object.(*mvccpb.KeyValue)
- consumerId, domainProject, data := pb.GetInfoFromTagKV(kv)
- if data == nil {
- util.Logger().Errorf(nil,
- "unmarshal service rule file failed, service %s tags [%s] event, data is nil",
- consumerId, action)
- return
- }
+ consumerId, domainProject := backend.GetInfoFromTagKV(evt.KV)
if nf.GetNotifyService().Closed() {
- util.Logger().Warnf(nil, "caught service %s tags [%s] event, but notify service is closed",
- consumerId, action)
- return
- }
- util.Logger().Infof("caught service %s tags [%s] event", consumerId, action)
-
- var rule pb.ServiceRule
- err := json.Unmarshal(data, &rule)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal service %s tags file failed", consumerId)
+ util.Logger().Warnf(nil, "caught [%s] service tags event %s/%s, but notify service is closed",
+ action, consumerId, evt.KV.Value)
return
}
+ util.Logger().Infof("caught [%s] service tags event %s/%s", action, consumerId, evt.KV.Value)
async.Service().Add(context.Background(),
NewTagsChangedAsyncTask(domainProject, consumerId, evt.Revision))
diff --git a/server/service/instance.go b/server/service/instance.go
index 4b909681..848291fe 100644
--- a/server/service/instance.go
+++ b/server/service/instance.go
@@ -28,6 +28,7 @@ import (
"github.com/apache/incubator-servicecomb-service-center/server/infra/quota"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
"github.com/apache/incubator-servicecomb-service-center/server/plugin"
+ "github.com/apache/incubator-servicecomb-service-center/server/service/cache"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
"golang.org/x/net/context"
"math"
@@ -537,91 +538,87 @@ func (s *InstanceService) Find(ctx context.Context, in *pb.FindInstancesRequest)
provider.Tenant = util.ParseTargetDomainProject(ctx)
}
+ noCache, cacheOnly := ctx.Value(serviceUtil.CTX_NOCACHE) == "1", ctx.Value(serviceUtil.CTX_CACHEONLY) == "1"
+ rev, _ := ctx.Value(serviceUtil.CTX_REQUEST_REVISION).(string)
+ reqRev, _ := serviceUtil.ParseRevision(rev)
+ cloneCtx := util.CloneContext(ctx)
+
// cache
- if item := serviceUtil.FindInstancesCache.Get(provider.Tenant, in.ConsumerServiceId, provider); item != nil {
- noCache, cacheOnly := ctx.Value(serviceUtil.CTX_NOCACHE) == "1", ctx.Value(serviceUtil.CTX_CACHEONLY) == "1"
- rev, _ := ctx.Value(serviceUtil.CTX_REQUEST_REVISION).(string)
- reqRev, _ := serviceUtil.ParseRevision(rev)
- cacheRev, _ := serviceUtil.ParseRevision(item.Rev)
- if !noCache && (cacheOnly || reqRev <= cacheRev) {
- instances := item.Instances
- if rev == item.Rev {
- instances = instances[:0]
- }
- util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, item.Rev)
+ var (
+ item *cache.VersionRuleCacheItem
+ i = 0
+ newVersionRule = !cache.DependencyRule.ExistVersionRule(cloneCtx, in.ConsumerServiceId, provider)
+ )
+ if noCache {
+ i = 1
+ }
+ for ; i < 2; i++ {
+ item, err = cache.FindInstances.Get(cloneCtx, service, provider, in.Tags)
+ if err != nil {
+ util.Logger().Errorf(err, "FindInstancesCache.Get failed, %s", findFlag)
return &pb.FindInstancesResponse{
- Response: pb.CreateResponse(pb.Response_SUCCESS, "Query service instances successfully."),
- Instances: instances,
+ Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
+ }, err
+ }
+ if item == nil {
+ mes := fmt.Sprintf("provider not exist, %s", findFlag)
+ util.Logger().Errorf(nil, "FindInstancesCache.Get failed, %s", mes)
+ return &pb.FindInstancesResponse{
+ Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
}, nil
}
- }
- // 版本规则
- ids, err := serviceUtil.FindServiceIds(ctx, in.VersionRule, provider)
- if err != nil {
- util.Logger().Errorf(err, "find instance failed, %s: get providers failed.", findFlag)
- return &pb.FindInstancesResponse{
- Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
- }, err
- }
-
- if len(ids) == 0 {
- mes := fmt.Sprintf("provider not exist, %s", findFlag)
- util.Logger().Errorf(nil, "find instance failed, %s", mes)
- return &pb.FindInstancesResponse{
- Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
- }, nil
- }
-
- for i := 0; i < len(ids); i++ {
- err := s.getInstancePreCheck(ctx, ids[i], in.ConsumerServiceId, in.Tags)
- if err != nil {
- ids = append(ids[:i], ids[i+1:]...)
- i--
+ cacheRev, _ := serviceUtil.ParseRevision(item.Rev)
+ if noCache || cacheOnly || reqRev <= cacheRev {
+ break
}
+ util.SetContext(cloneCtx, serviceUtil.CTX_NOCACHE, "1")
}
- if len(ids) > 0 {
- //维护version的规则,service name 可能是别名,所以重新获取
- providerService, err := serviceUtil.GetService(ctx, provider.Tenant, ids[0])
- if providerService == nil {
- util.Logger().Errorf(err, "find instance failed, %s: provider %s not exist.", findFlag, ids[0])
+ // add dependency queue
+ if newVersionRule {
+ provider, err = s.reshapeProviderKey(ctx, provider, item.ServiceIds[0])
+ if provider != nil {
+ err = serviceUtil.AddServiceVersionRule(ctx, domainProject, service, provider)
+ } else {
+ mes := fmt.Sprintf("provider not exist, %s", findFlag)
+ util.Logger().Errorf(nil, "AddServiceVersionRule failed, %s", mes)
return &pb.FindInstancesResponse{
- Response: pb.CreateResponse(scerr.ErrServiceNotExists, "No provider matched."),
+ Response: pb.CreateResponse(scerr.ErrServiceNotExists, mes),
}, nil
}
-
- provider = pb.MicroServiceToKey(provider.Tenant, providerService)
- provider.Version = in.VersionRule
-
- err = serviceUtil.AddServiceVersionRule(ctx, domainProject, service, provider)
if err != nil {
- util.Logger().Errorf(err, "find instance failed, %s: add service version rule failed.", findFlag)
+ util.Logger().Errorf(err, "AddServiceVersionRule failed, %s", findFlag)
return &pb.FindInstancesResponse{
Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
}, err
}
}
- instances, rev, err := serviceUtil.GetAllInstancesOfServices(ctx, util.ParseTargetDomainProject(ctx), ids)
- if err != nil {
- util.Logger().Errorf(err, "find instance failed, %s: GetAllInstancesOfServices failed.", findFlag)
- return &pb.FindInstancesResponse{
- Response: pb.CreateResponse(scerr.ErrInternal, err.Error()),
- }, err
+ instances := item.Instances
+ if rev == item.Rev {
+ instances = instances[:0]
}
-
- serviceUtil.FindInstancesCache.Set(provider.Tenant, in.ConsumerServiceId, provider, &serviceUtil.VersionRuleCacheItem{
- Instances: instances,
- Rev: rev,
- })
- util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, rev)
+ util.SetContext(ctx, serviceUtil.CTX_RESPONSE_REVISION, item.Rev)
return &pb.FindInstancesResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Query service instances successfully."),
Instances: instances,
}, nil
}
+func (s *InstanceService) reshapeProviderKey(ctx context.Context, provider *pb.MicroServiceKey, providerId string) (*pb.MicroServiceKey, error) {
+ //维护version的规则,service name 可能是别名,所以重新获取
+ providerService, err := serviceUtil.GetService(ctx, provider.Tenant, providerId)
+ if providerService == nil {
+ return nil, err
+ }
+
+ versionRule := provider.Version
+ provider = pb.MicroServiceToKey(provider.Tenant, providerService)
+ provider.Version = versionRule
+ return provider, nil
+}
+
func (s *InstanceService) UpdateStatus(ctx context.Context, in *pb.UpdateInstanceStatusRequest) (*pb.UpdateInstanceStatusResponse, error) {
domainProject := util.ParseDomainProject(ctx)
updateStatusFlag := util.StringJoin([]string{in.ServiceId, in.InstanceId, in.Status}, "/")
diff --git a/server/service/instance_test.go b/server/service/instance_test.go
index c11866e4..692e4d19 100644
--- a/server/service/instance_test.go
+++ b/server/service/instance_test.go
@@ -1235,8 +1235,7 @@ var _ = Describe("'Instance' service", func() {
VersionRule: "1.0.5",
})
Expect(err).To(BeNil())
- Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
- Expect(len(respFind.Instances)).To(Equal(0))
+ Expect(respFind.Response.Code).To(Equal(scerr.ErrServiceNotExists))
By("provider tag does not exist")
respFind, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
@@ -1247,8 +1246,7 @@ var _ = Describe("'Instance' service", func() {
Tags: []string{"notexisttag"},
})
Expect(err).To(BeNil())
- Expect(respFind.Response.Code).To(Equal(pb.Response_SUCCESS))
- Expect(len(respFind.Instances)).To(Equal(0))
+ Expect(respFind.Response.Code).To(Equal(scerr.ErrServiceNotExists))
By("shared service discovery")
os.Setenv("CSE_SHARED_SERVICES", "query_instance_shared_provider")
diff --git a/server/service/microservice.go b/server/service/microservice.go
index 2136f8ca..b5046485 100644
--- a/server/service/microservice.go
+++ b/server/service/microservice.go
@@ -189,7 +189,7 @@ func (s *MicroServiceService) CreateServicePri(ctx context.Context, in *pb.Creat
func checkQuota(ctx context.Context, domainProject string) (quota.QuotaReporter, *scerr.Error) {
if core.IsSCInstance(ctx) {
- util.Logger().Infof("it is service-center")
+ util.Logger().Debugf("service-center self register")
return nil, nil
}
res := quota.NewApplyQuotaResource(quota.MicroServiceQuotaType, domainProject, "", 1)
diff --git a/server/service/notification/common.go b/server/service/notification/common.go
index 7e2e16a3..7b0728cf 100644
--- a/server/service/notification/common.go
+++ b/server/service/notification/common.go
@@ -17,17 +17,17 @@
package notification
import (
- "fmt"
"strconv"
"time"
)
const (
- DEFAULT_MAX_QUEUE = 1000
- DEFAULT_INIT_SUBSCRIBERS = 1000
- DEFAULT_ON_MESSAGE_TIMEOUT = 100 * time.Millisecond
- DEFAULT_TIMEOUT = 30 * time.Second
+ DEFAULT_MAX_QUEUE = 1000
+ DEFAULT_ADD_JOB_TIMEOUT = 1 * time.Second
+ DEFAULT_SEND_TIMEOUT = 30 * time.Second
+)
+const (
NOTIFTY NotifyType = iota
INSTANCE
typeEnd
@@ -42,13 +42,21 @@ func (nt NotifyType) String() string {
return "NotifyType" + strconv.Itoa(int(nt))
}
-type NotifyServiceConfig struct {
- AddTimeout time.Duration
- NotifyTimeout time.Duration
- MaxQueue int64
+func (nt NotifyType) QueueSize() (s int) {
+ if int(nt) < len(notifyTypeQueues) {
+ s = notifyTypeQueues[nt]
+ }
+ if s <= 0 {
+ s = DEFAULT_MAX_QUEUE
+ }
+ return
+}
+
+var notifyTypeNames = []string{
+ NOTIFTY: "NOTIFTY",
+ INSTANCE: "INSTANCE",
}
-func (nsc NotifyServiceConfig) String() string {
- return fmt.Sprintf("{acceptQueue: %d, accept: %s, notify: %s}",
- nsc.MaxQueue, nsc.AddTimeout, nsc.NotifyTimeout)
+var notifyTypeQueues = []int{
+ INSTANCE: 100 * 1000,
}
diff --git a/server/service/notification/group.go b/server/service/notification/group.go
new file mode 100644
index 00000000..679acfd6
--- /dev/null
+++ b/server/service/notification/group.go
@@ -0,0 +1,64 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+)
+
+type Group struct {
+ name string
+ subscribers *util.ConcurrentMap
+}
+
+func (g *Group) Name() string {
+ return g.name
+}
+
+func (g *Group) Notify(job NotifyJob) {
+ g.subscribers.ForEach(func(item util.MapItem) (next bool) {
+ item.Value.(Subscriber).OnMessage(job)
+ return true
+ })
+}
+
+func (g *Group) Subscribers(name string) Subscriber {
+ s, ok := g.subscribers.Get(name)
+ if !ok {
+ return nil
+ }
+ return s.(Subscriber)
+}
+
+func (g *Group) AddSubscriber(subscriber Subscriber) Subscriber {
+ return g.subscribers.PutIfAbsent(subscriber.Id(), subscriber).(Subscriber)
+}
+
+func (g *Group) Remove(name string) {
+ g.subscribers.Remove(name)
+}
+
+func (g *Group) Size() int {
+ return g.subscribers.Size()
+}
+
+func NewGroup(name string) *Group {
+ return &Group{
+ name: name,
+ subscribers: util.NewConcurrentMap(0),
+ }
+}
diff --git a/server/service/notification/group_test.go b/server/service/notification/group_test.go
new file mode 100644
index 00000000..b241eb48
--- /dev/null
+++ b/server/service/notification/group_test.go
@@ -0,0 +1,69 @@
+/*
+ * 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 notification
+
+import "testing"
+
+type mockSubscriber struct {
+ *BaseSubscriber
+ job NotifyJob
+}
+
+func (s *mockSubscriber) OnMessage(job NotifyJob) {
+ s.job = job
+}
+
+func TestGroup_Add(t *testing.T) {
+ m := NewSubscriber(INSTANCE, "s1", "g1")
+ g := NewGroup("g1")
+ if g.Name() != "g1" {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ if g.AddSubscriber(m) != m {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ if g.AddSubscriber(NewSubscriber(INSTANCE, "s1", "g1")) == m {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ same := *m
+ if g.AddSubscriber(&same) != m {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ if g.Size() != 2 {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ g.Remove(m.Id())
+ if g.Size() != 1 {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ if g.Subscribers(m.Id()) == m {
+ t.Fatalf("TestGroup_Add failed")
+ }
+
+ mock := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+ if g.AddSubscriber(mock) != mock {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ if g.Subscribers(mock.Id()) != mock {
+ t.Fatalf("TestGroup_Add failed")
+ }
+ job := &BaseNotifyJob{nType: INSTANCE}
+ g.Notify(job)
+ if mock.job != job {
+ t.Fatalf("TestGroup_Add failed")
+ }
+}
diff --git a/server/service/notification/listwatcher.go b/server/service/notification/listwatcher.go
index a6942c92..0877b27f 100644
--- a/server/service/notification/listwatcher.go
+++ b/server/service/notification/listwatcher.go
@@ -25,26 +25,30 @@ import (
// 状态变化推送
type WatchJob struct {
- BaseNotifyJob
+ *BaseNotifyJob
Revision int64
Response *pb.WatchInstanceResponse
}
type ListWatcher struct {
- BaseSubscriber
+ *BaseSubscriber
Job chan *WatchJob
ListRevision int64
ListFunc func() (results []*pb.WatchInstanceResponse, rev int64)
+ listCh chan struct{}
+}
- listCh chan struct{}
+func (s *ListWatcher) SetError(err error) {
+ s.BaseSubscriber.SetError(err)
+ // 触发清理job
+ s.Service().AddJob(NewNotifyServiceHealthCheckJob(s))
}
func (w *ListWatcher) OnAccept() {
if w.Err() != nil {
return
}
-
- util.Logger().Debugf("accepted by notify service, %s watcher %s %s", w.Type(), w.Id(), w.Subject())
+ util.Logger().Debugf("accepted by notify service, %s watcher %s %s", w.Type(), w.Group(), w.Subject())
util.Go(w.listAndPublishJobs)
}
@@ -56,7 +60,7 @@ func (w *ListWatcher) listAndPublishJobs(_ context.Context) {
results, rev := w.ListFunc()
w.ListRevision = rev
for _, response := range results {
- w.sendMessage(NewWatchJob(w.Id(), w.Subject(), w.ListRevision, response))
+ w.sendMessage(NewWatchJob(w.Group(), w.Subject(), w.ListRevision, response))
}
}
@@ -71,68 +75,73 @@ func (w *ListWatcher) OnMessage(job NotifyJob) {
return
}
- timer := time.NewTimer(DEFAULT_ON_MESSAGE_TIMEOUT)
select {
case <-w.listCh:
- timer.Stop()
- case <-timer.C:
- util.Logger().Errorf(nil,
- "the %s listwatcher %s %s is not ready[over %s], drop the event %v",
- w.Type(), w.Id(), w.Subject(), DEFAULT_ON_MESSAGE_TIMEOUT, job)
- return
+ default:
+ timer := time.NewTimer(w.Timeout())
+ select {
+ case <-w.listCh:
+ timer.Stop()
+ case <-timer.C:
+ util.Logger().Errorf(nil,
+ "the %s listwatcher %s %s is not ready[over %s], send the event %v",
+ w.Type(), w.Group(), w.Subject(), w.Timeout(), job)
+ }
}
if wJob.Revision <= w.ListRevision {
util.Logger().Warnf(nil,
"unexpected notify %s job is coming in, watcher %s %s, job is %v, current revision is %v",
- w.Type(), w.Id(), w.Subject(), job, w.ListRevision)
+ w.Type(), w.Group(), w.Subject(), job, w.ListRevision)
return
}
w.sendMessage(wJob)
}
func (w *ListWatcher) sendMessage(job *WatchJob) {
- util.Logger().Debugf("start to notify %s watcher %s %s, job is %v, current revision is %v", w.Type(),
- w.Id(), w.Subject(), job, w.ListRevision)
defer util.RecoverAndReport()
- timer := time.NewTimer(DEFAULT_ON_MESSAGE_TIMEOUT)
select {
case w.Job <- job:
- timer.Stop()
- case <-timer.C:
- util.Logger().Errorf(nil,
- "the %s watcher %s %s event queue is full[over %s], drop the event %v",
- w.Type(), w.Id(), w.Subject(), DEFAULT_ON_MESSAGE_TIMEOUT, job)
+ default:
+ timer := time.NewTimer(w.Timeout())
+ select {
+ case w.Job <- job:
+ timer.Stop()
+ case <-timer.C:
+ util.Logger().Errorf(nil,
+ "the %s watcher %s %s event queue is full[over %s], drop the event %v",
+ w.Type(), w.Group(), w.Subject(), w.Timeout(), job)
+ }
}
}
+func (w *ListWatcher) Timeout() time.Duration {
+ return DEFAULT_ADD_JOB_TIMEOUT
+}
+
func (w *ListWatcher) Close() {
close(w.Job)
}
-func NewWatchJob(subscriberId, subject string, rev int64, response *pb.WatchInstanceResponse) *WatchJob {
+func NewWatchJob(group, subject string, rev int64, response *pb.WatchInstanceResponse) *WatchJob {
return &WatchJob{
- BaseNotifyJob: BaseNotifyJob{
- subscriberId: subscriberId,
- subject: subject,
- nType: INSTANCE,
+ BaseNotifyJob: &BaseNotifyJob{
+ group: group,
+ subject: subject,
+ nType: INSTANCE,
},
Revision: rev,
Response: response,
}
}
-func NewListWatcher(id string, subject string,
+func NewListWatcher(group string, subject string,
listFunc func() (results []*pb.WatchInstanceResponse, rev int64)) *ListWatcher {
watcher := &ListWatcher{
- BaseSubscriber: BaseSubscriber{
- id: id,
- subject: subject,
- nType: INSTANCE,
- },
- Job: make(chan *WatchJob, DEFAULT_MAX_QUEUE),
- ListFunc: listFunc,
- listCh: make(chan struct{}),
+ BaseSubscriber: NewSubscriber(INSTANCE, subject, group),
+ Job: make(chan *WatchJob, DEFAULT_MAX_QUEUE),
+ ListFunc: listFunc,
+ listCh: make(chan struct{}),
}
return watcher
}
diff --git a/server/service/notification/notice.go b/server/service/notification/notice.go
index 498d435c..26d52352 100644
--- a/server/service/notification/notice.go
+++ b/server/service/notification/notice.go
@@ -17,25 +17,25 @@
package notification
type NotifyJob interface {
- SubscriberId() string
- Subject() string
Type() NotifyType
+ Group() string
+ Subject() string
}
type BaseNotifyJob struct {
- subscriberId string
- subject string
- nType NotifyType
+ nType NotifyType
+ subject string
+ group string
}
-func (s *BaseNotifyJob) SubscriberId() string {
- return s.subscriberId
+func (s *BaseNotifyJob) Type() NotifyType {
+ return s.nType
}
-func (s *BaseNotifyJob) Subject() string {
- return s.subject
+func (s *BaseNotifyJob) Group() string {
+ return s.group
}
-func (s *BaseNotifyJob) Type() NotifyType {
- return s.nType
+func (s *BaseNotifyJob) Subject() string {
+ return s.subject
}
diff --git a/server/service/notification/notification_healthchecker.go b/server/service/notification/notification_healthchecker.go
index 310275b7..597713c0 100644
--- a/server/service/notification/notification_healthchecker.go
+++ b/server/service/notification/notification_healthchecker.go
@@ -29,7 +29,7 @@ type NotifyServiceHealthChecker struct {
}
type NotifyServiceHealthCheckJob struct {
- BaseNotifyJob
+ *BaseNotifyJob
ErrorSubscriber Subscriber
}
@@ -39,19 +39,19 @@ func (s *NotifyServiceHealthChecker) OnMessage(job NotifyJob) {
if j.ErrorSubscriber.Type() == NOTIFTY {
util.Logger().Errorf(nil, "remove %s watcher %s %s failed, here cause a dead lock",
- j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Id())
+ j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Group())
return
}
- util.Logger().Warnf(err, "notification service remove %s watcher %s %s",
- j.ErrorSubscriber.Type(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Id())
+ util.Logger().Debugf("notification service remove %s watcher, error: %s, subject: %s, group: %s",
+ j.ErrorSubscriber.Type(), err.Error(), j.ErrorSubscriber.Subject(), j.ErrorSubscriber.Group())
s.Service().RemoveSubscriber(j.ErrorSubscriber)
}
func NewNotifyServiceHealthChecker() *NotifyServiceHealthChecker {
return &NotifyServiceHealthChecker{
BaseSubscriber: BaseSubscriber{
- id: NOTIFY_SERVER_CHECKER_NAME,
+ group: NOTIFY_SERVER_CHECKER_NAME,
subject: NOTIFY_SERVER_CHECK_SUBJECT,
nType: NOTIFTY,
},
@@ -60,10 +60,10 @@ func NewNotifyServiceHealthChecker() *NotifyServiceHealthChecker {
func NewNotifyServiceHealthCheckJob(s Subscriber) *NotifyServiceHealthCheckJob {
return &NotifyServiceHealthCheckJob{
- BaseNotifyJob: BaseNotifyJob{
- subscriberId: NOTIFY_SERVER_CHECKER_NAME,
- subject: NOTIFY_SERVER_CHECK_SUBJECT,
- nType: NOTIFTY,
+ BaseNotifyJob: &BaseNotifyJob{
+ group: NOTIFY_SERVER_CHECKER_NAME,
+ subject: NOTIFY_SERVER_CHECK_SUBJECT,
+ nType: NOTIFTY,
},
ErrorSubscriber: s,
}
diff --git a/server/service/notification/notification_service.go b/server/service/notification/notification_service.go
index 82cc9b11..a5ea4ea4 100644
--- a/server/service/notification/notification_service.go
+++ b/server/service/notification/notification_service.go
@@ -17,19 +17,12 @@
package notification
import (
- "container/list"
"errors"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
"golang.org/x/net/context"
"sync"
- "time"
)
-var notifyTypeNames = []string{
- NOTIFTY: "NOTIFTY",
- INSTANCE: "INSTANCE",
-}
-
var notifyService *NotifyService
func init() {
@@ -39,102 +32,78 @@ func init() {
}
}
-type subscriberIndex map[string]*list.List
-
-type subscriberSubjectIndex map[string]subscriberIndex
-
-type serviceIndex map[NotifyType]subscriberSubjectIndex
-
type NotifyService struct {
- Config NotifyServiceConfig
-
- services serviceIndex
- queues map[NotifyType]chan NotifyJob
- waits sync.WaitGroup
- mutexes map[NotifyType]*sync.Mutex
- err chan error
- closeMux sync.RWMutex
- isClose bool
- goroutine *util.GoRoutine
+ processors *util.ConcurrentMap
+ goroutine *util.GoRoutine
+ err chan error
+ closeMux sync.RWMutex
+ isClose bool
}
func (s *NotifyService) Err() <-chan error {
return s.err
}
-func (s *NotifyService) AddSubscriber(n Subscriber) error {
- if s.Closed() {
- return errors.New("server is shutting down")
+func (s *NotifyService) init() {
+ s.processors = util.NewConcurrentMap(int(typeEnd))
+ s.err = make(chan error, 1)
+ for i := NotifyType(0); i != typeEnd; i++ {
+ s.processors.Put(i, NewProcessor(i.String(), i.QueueSize()))
}
+}
- s.mutexes[n.Type()].Lock()
- ss, ok := s.services[n.Type()]
- if !ok {
- s.mutexes[n.Type()].Unlock()
- return errors.New("Unknown subscribe type")
+func (s *NotifyService) Start() {
+ if !s.Closed() {
+ util.Logger().Warnf(nil, "notify service is already running")
+ return
}
+ s.closeMux.Lock()
+ s.isClose = false
+ s.closeMux.Unlock()
- sr, ok := ss[n.Subject()]
- if !ok {
- sr = make(subscriberIndex, DEFAULT_INIT_SUBSCRIBERS)
- ss[n.Subject()] = sr // add a subscriber
+ s.init()
+ // 错误subscriber清理
+ s.AddSubscriber(NewNotifyServiceHealthChecker())
+
+ util.Logger().Debugf("notify service is started")
+
+ s.processors.ForEach(func(item util.MapItem) (next bool) {
+ s.goroutine.Do(item.Value.(*Processor).Do)
+ return true
+ })
+}
+
+func (s *NotifyService) AddSubscriber(n Subscriber) error {
+ if s.Closed() {
+ return errors.New("server is shutting down")
}
- ns, ok := sr[n.Id()]
+ itf, ok := s.processors.Get(n.Type())
if !ok {
- ns = list.New()
+ return errors.New("Unknown subscribe type")
}
- ns.PushBack(n) // add a connection
- sr[n.Id()] = ns
-
n.SetService(s)
- s.mutexes[n.Type()].Unlock()
-
n.OnAccept()
+
+ itf.(*Processor).AddSubscriber(n)
return nil
}
func (s *NotifyService) RemoveSubscriber(n Subscriber) {
- s.mutexes[n.Type()].Lock()
- defer s.mutexes[n.Type()].Unlock()
- ss, ok := s.services[n.Type()]
- if !ok {
- return
- }
-
- m, ok := ss[n.Subject()]
- if !ok {
- return
- }
-
- ns, ok := m[n.Id()]
+ itf, ok := s.processors.Get(n.Type())
if !ok {
return
}
- for sr := ns.Front(); sr != nil; sr = sr.Next() {
- if sr.Value == n {
- ns.Remove(sr)
- n.Close()
- break
- }
- }
+ itf.(*Processor).Remove(n)
+ n.Close()
}
func (s *NotifyService) RemoveAllSubscribers() {
- for t, ss := range s.services {
- s.mutexes[t].Lock()
- for _, subscribers := range ss {
- for _, ns := range subscribers {
- for e, n := ns.Front(), ns.Front(); e != nil; e = n {
- e.Value.(Subscriber).Close()
- n = e.Next()
- ns.Remove(e)
- }
- }
- }
- s.mutexes[t].Unlock()
- }
+ s.processors.ForEach(func(item util.MapItem) (next bool) {
+ item.Value.(*Processor).Clear()
+ return true
+ })
}
//通知内容塞到队列里
@@ -143,109 +112,12 @@ func (s *NotifyService) AddJob(job NotifyJob) error {
return errors.New("add notify job failed for server shutdown")
}
- defer util.RecoverAndReport()
-
- timer := time.NewTimer(s.Config.AddTimeout)
- select {
- case s.queues[job.Type()] <- job:
- timer.Stop()
- return nil
- case <-timer.C:
- util.Logger().Errorf(nil, "Add job failed.%s")
- return errors.New("add notify job timeout")
- }
-}
-
-func (s *NotifyService) getPublish2SubscriberFunc(t NotifyType) func(context.Context) {
- return func(ctx context.Context) {
- defer s.waits.Done()
- for {
- select {
- case <-ctx.Done():
- return
- case job, ok := <-s.queues[t]:
- if !ok {
- return
- }
-
- util.Logger().Infof("notification service got a job %s: %s to notify subscriber %s",
- job.Type(), job.Subject(), job.SubscriberId())
-
- s.mutexes[t].Lock()
-
- if s.Closed() && len(s.services[t]) == 0 {
- s.mutexes[t].Unlock()
- return
- }
-
- m, ok := s.services[t][job.Subject()]
- if ok {
- // publish的subject如果带上id,则单播,否则广播
- if len(job.SubscriberId()) != 0 {
- ns, ok := m[job.SubscriberId()]
- if ok {
- for n := ns.Front(); n != nil; n = n.Next() {
- n.Value.(Subscriber).OnMessage(job)
- }
- }
- s.mutexes[t].Unlock()
- continue
- }
- for key := range m {
- ns := m[key]
- for n := ns.Front(); n != nil; n = n.Next() {
- n.Value.(Subscriber).OnMessage(job)
- }
- }
- }
-
- s.mutexes[t].Unlock()
- }
- }
- }
-}
-
-func (s *NotifyService) init() {
- if s.Config.AddTimeout <= 0 {
- s.Config.AddTimeout = DEFAULT_TIMEOUT
- }
- if s.Config.NotifyTimeout <= 0 {
- s.Config.NotifyTimeout = DEFAULT_TIMEOUT
- }
- if s.Config.MaxQueue <= 0 || s.Config.MaxQueue > DEFAULT_MAX_QUEUE {
- s.Config.MaxQueue = DEFAULT_MAX_QUEUE
- }
-
- s.services = make(serviceIndex, typeEnd)
- s.err = make(chan error, 1)
- s.queues = make(map[NotifyType]chan NotifyJob, typeEnd)
- s.mutexes = make(map[NotifyType]*sync.Mutex, typeEnd)
- for i := NotifyType(0); i != typeEnd; i++ {
- s.services[i] = make(subscriberSubjectIndex, DEFAULT_INIT_SUBSCRIBERS)
- s.queues[i] = make(chan NotifyJob, s.Config.MaxQueue)
- s.mutexes[i] = &sync.Mutex{}
- s.waits.Add(1)
- }
-}
-
-func (s *NotifyService) Start() {
- if !s.Closed() {
- util.Logger().Warnf(nil, "notify service is already running with config %s", s.Config)
- return
- }
- s.closeMux.Lock()
- s.isClose = false
- s.closeMux.Unlock()
-
- s.init()
- // 错误subscriber清理
- s.AddSubscriber(NewNotifyServiceHealthChecker())
-
- util.Logger().Debugf("notify service is started with config %s", s.Config)
-
- for i := NotifyType(0); i != typeEnd; i++ {
- s.goroutine.Do(s.getPublish2SubscriberFunc(i))
+ itf, ok := s.processors.Get(job.Type())
+ if !ok {
+ return errors.New("Unknown job type")
}
+ itf.(*Processor).Accept(job)
+ return nil
}
func (s *NotifyService) Closed() (b bool) {
@@ -263,17 +135,12 @@ func (s *NotifyService) Stop() {
s.isClose = true
s.closeMux.Unlock()
- for _, c := range s.queues {
- close(c)
- }
- s.waits.Wait()
+ s.goroutine.Close(true)
s.RemoveAllSubscribers()
close(s.err)
- s.goroutine.Close(true)
-
util.Logger().Debug("notify service stopped.")
}
diff --git a/server/service/notification/notification_test.go b/server/service/notification/notification_test.go
new file mode 100644
index 00000000..d7da51e5
--- /dev/null
+++ b/server/service/notification/notification_test.go
@@ -0,0 +1,86 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "golang.org/x/net/context"
+ "testing"
+ "time"
+)
+
+func TestGetNotifyService(t *testing.T) {
+ notifyService := &NotifyService{
+ isClose: true,
+ goroutine: util.NewGo(context.Background()),
+ }
+ if notifyService == nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ if notifyService.Closed() != true {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+
+ err := notifyService.AddSubscriber(nil)
+ if err == nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ err = notifyService.AddJob(nil)
+ if err == nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+
+ notifyService.Start()
+ notifyService.Start()
+ if notifyService.Closed() != false {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ select {
+ case <-notifyService.Err():
+ t.Fatalf("TestGetNotifyService failed")
+ default:
+ }
+
+ s := NewSubscriber(-1, "s", "g")
+ err = notifyService.AddSubscriber(s)
+ if err == nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ s = NewSubscriber(INSTANCE, "s", "g")
+ err = notifyService.AddSubscriber(s)
+ if err != nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ j := &BaseNotifyJob{INSTANCE, "s", "g"}
+ err = notifyService.AddJob(j)
+ if err != nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ err = notifyService.AddJob(NewNotifyServiceHealthCheckJob(NewNotifyServiceHealthChecker()))
+ if err != nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ <-time.After(time.Second)
+ err = notifyService.AddJob(NewNotifyServiceHealthCheckJob(s))
+ if err != nil {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+ notifyService.Stop()
+ if notifyService.Closed() != true {
+ t.Fatalf("TestGetNotifyService failed")
+ }
+}
diff --git a/server/service/notification/processor.go b/server/service/notification/processor.go
new file mode 100644
index 00000000..7fe27f1d
--- /dev/null
+++ b/server/service/notification/processor.go
@@ -0,0 +1,106 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "golang.org/x/net/context"
+)
+
+type Processor struct {
+ name string
+ subjects *util.ConcurrentMap
+ queue chan NotifyJob
+}
+
+func (p *Processor) Name() string {
+ return p.name
+}
+
+func (p *Processor) Notify(job NotifyJob) {
+ if itf, ok := p.subjects.Get(job.Subject()); ok {
+ itf.(*Subject).Notify(job)
+ }
+}
+
+func (p *Processor) Subjects(name string) *Subject {
+ itf, ok := p.subjects.Get(name)
+ if !ok {
+ return nil
+ }
+ return itf.(*Subject)
+}
+
+func (p *Processor) AddSubscriber(n Subscriber) {
+ item, _ := p.subjects.Fetch(n.Subject(), func() (interface{}, error) {
+ return NewSubject(n.Subject()), nil
+ })
+ item.(*Subject).GetOrNewGroup(n.Group()).AddSubscriber(n)
+}
+
+func (p *Processor) Remove(n Subscriber) {
+ itf, ok := p.subjects.Get(n.Subject())
+ if !ok {
+ return
+ }
+
+ s := itf.(*Subject)
+ g := s.Groups(n.Group())
+ if g == nil {
+ return
+ }
+
+ g.Remove(n.Id())
+
+ if g.Size() == 0 {
+ s.Remove(g.Name())
+ }
+ if s.Size() == 0 {
+ p.subjects.Remove(s.Name())
+ }
+}
+
+func (p *Processor) Clear() {
+ p.subjects.Clear()
+}
+
+func (p *Processor) Accept(job NotifyJob) {
+ defer util.RecoverAndReport()
+ p.queue <- job
+}
+
+func (p *Processor) Do(ctx context.Context) {
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case job, ok := <-p.queue:
+ if !ok {
+ return
+ }
+ p.Notify(job)
+ }
+ }
+}
+
+func NewProcessor(name string, queue int) *Processor {
+ return &Processor{
+ name: name,
+ subjects: util.NewConcurrentMap(0),
+ queue: make(chan NotifyJob, queue),
+ }
+}
diff --git a/server/service/notification/processor_test.go b/server/service/notification/processor_test.go
new file mode 100644
index 00000000..da4bd860
--- /dev/null
+++ b/server/service/notification/processor_test.go
@@ -0,0 +1,74 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "testing"
+)
+
+func TestProcessor_Do(t *testing.T) {
+ mock1 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+ mock2 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g2")}
+ p := NewProcessor("p1", 0)
+ util.Go(p.Do)
+ if p.Name() != "p1" {
+ t.Fatalf("TestProcessor_Do")
+ }
+ if p.Subjects(mock1.Subject()) != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ p.AddSubscriber(mock1)
+ if p.Subjects(mock1.Subject()).Groups(mock1.Group()).Subscribers(mock1.Id()) != mock1 {
+ t.Fatalf("TestProcessor_Do")
+ }
+ p.Remove(NewSubscriber(INSTANCE, "s2", "g1"))
+ p.Remove(NewSubscriber(INSTANCE, "s1", "g2"))
+ p.Remove(mock1)
+ if p.Subjects(mock1.Subject()) != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ p.AddSubscriber(mock1)
+ p.Clear()
+ if p.Subjects(mock1.Subject()) != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ p.AddSubscriber(mock1)
+ job := &BaseNotifyJob{group: "g1"}
+ p.Accept(job)
+ if mock1.job != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ job.subject = "s1"
+ job.group = "g3"
+ p.Accept(job)
+ if mock1.job != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ job.subject = "s1"
+ job.group = "g1"
+ p.Accept(job)
+ if mock1.job != job || mock2.job != nil {
+ t.Fatalf("TestProcessor_Do")
+ }
+ job.subject = "s1"
+ job.group = ""
+ p.Accept(job)
+ if mock1.job != job && mock2.job != job {
+ t.Fatalf("TestProcessor_Do")
+ }
+}
diff --git a/server/service/notification/publisher.go b/server/service/notification/publisher.go
new file mode 100644
index 00000000..17bab452
--- /dev/null
+++ b/server/service/notification/publisher.go
@@ -0,0 +1,103 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ "golang.org/x/net/context"
+ "sync"
+ "time"
+)
+
+var publisher *Publisher
+
+func init() {
+ publisher = NewPublisher()
+ publisher.Run()
+}
+
+type Publisher struct {
+ wss []*WebSocket
+ lock sync.Mutex
+ goroutine *util.GoRoutine
+}
+
+func (wh *Publisher) Run() {
+ util.Go(publisher.loop)
+}
+
+func (wh *Publisher) Stop() {
+ wh.goroutine.Close(true)
+}
+
+func (wh *Publisher) dispatch(ws *WebSocket, payload interface{}) {
+ wh.goroutine.Do(func(ctx context.Context) {
+ ws.HandleWatchWebSocketJob(payload)
+ })
+}
+
+func (wh *Publisher) loop(ctx context.Context) {
+ defer wh.Stop()
+ ticker := time.NewTicker(500 * time.Millisecond)
+ for {
+ select {
+ case <-ctx.Done():
+ // server shutdown
+ return
+ case <-ticker.C:
+ var removes []int
+ for i, ws := range wh.wss {
+ if payload := ws.Pick(); payload != nil {
+ if _, ok := payload.(error); ok {
+ removes = append(removes, i)
+ }
+ wh.dispatch(ws, payload)
+ }
+ }
+ if len(removes) == 0 {
+ continue
+ }
+
+ wh.lock.Lock()
+ var (
+ news []*WebSocket
+ s int
+ )
+ for _, e := range removes {
+ news = append(news, wh.wss[s:e]...)
+ s = e + 1
+ }
+ if s < len(wh.wss) {
+ news = append(news, wh.wss[s:]...)
+ }
+ wh.wss = news
+ wh.lock.Unlock()
+ }
+ }
+}
+
+func (wh *Publisher) Accept(ws *WebSocket) {
+ wh.lock.Lock()
+ wh.wss = append(wh.wss, ws)
+ wh.lock.Unlock()
+}
+
+func NewPublisher() *Publisher {
+ return &Publisher{
+ goroutine: util.NewGo(context.Background()),
+ }
+}
diff --git a/server/service/notification/stream.go b/server/service/notification/stream.go
index 101473de..b9bbea27 100644
--- a/server/service/notification/stream.go
+++ b/server/service/notification/stream.go
@@ -23,32 +23,35 @@ import (
"time"
)
-func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchServer, timeout time.Duration) (err error) {
+func HandleWatchJob(watcher *ListWatcher, stream pb.ServiceInstanceCtrl_WatchServer) (err error) {
+ timer := time.NewTimer(DEFAULT_SEND_TIMEOUT)
+ defer timer.Stop()
for {
- timer := time.NewTimer(timeout)
select {
case <-timer.C:
+ timer.Reset(DEFAULT_SEND_TIMEOUT)
+
// TODO grpc 长连接心跳?
case job := <-watcher.Job:
- timer.Stop()
-
if job == nil {
err = errors.New("channel is closed")
- util.Logger().Errorf(err, "watcher %s %s caught an exception",
- watcher.Subject(), watcher.Id())
+ util.Logger().Errorf(err, "watcher caught an exception, subject: %s, group: %s",
+ watcher.Subject(), watcher.Group())
return
}
resp := job.Response
- util.Logger().Infof("event is coming in, watcher %s %s",
- watcher.Subject(), watcher.Id())
+ util.Logger().Infof("event is coming in, watcher, subject: %s, group: %s",
+ watcher.Subject(), watcher.Group())
err = stream.Send(resp)
if err != nil {
- util.Logger().Errorf(err, "send message error, watcher %s %s",
- watcher.Subject(), watcher.Id())
+ util.Logger().Errorf(err, "send message error, subject: %s, group: %s",
+ watcher.Subject(), watcher.Group())
watcher.SetError(err)
return
}
+
+ util.ResetTimer(timer, DEFAULT_SEND_TIMEOUT)
}
}
}
diff --git a/server/service/notification/stream_test.go b/server/service/notification/stream_test.go
new file mode 100644
index 00000000..51930454
--- /dev/null
+++ b/server/service/notification/stream_test.go
@@ -0,0 +1,34 @@
+/*
+ * 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 notification
+
+import "testing"
+
+func TestHandleWatchJob(t *testing.T) {
+ defer func() { recover() }()
+ w := NewListWatcher("g", "s", nil)
+ w.Job <- nil
+ err := HandleWatchJob(w, nil)
+ if err == nil {
+ t.Fatalf("TestHandleWatchJob failed")
+ }
+ w.Job <- NewWatchJob("g", "s", 1, nil)
+ err = HandleWatchJob(w, nil)
+ if err != nil {
+ t.Fatalf("TestHandleWatchJob failed")
+ }
+}
diff --git a/server/service/notification/subject.go b/server/service/notification/subject.go
new file mode 100644
index 00000000..2e2ce571
--- /dev/null
+++ b/server/service/notification/subject.go
@@ -0,0 +1,76 @@
+/*
+ * 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 notification
+
+import (
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+)
+
+type Subject struct {
+ name string
+ groups *util.ConcurrentMap
+}
+
+func (s *Subject) Name() string {
+ return s.name
+}
+
+func (s *Subject) Notify(job NotifyJob) {
+ if len(job.Group()) == 0 {
+ s.groups.ForEach(func(item util.MapItem) (next bool) {
+ item.Value.(*Group).Notify(job)
+ return true
+ })
+ return
+ }
+
+ itf, ok := s.groups.Get(job.Group())
+ if !ok {
+ return
+ }
+ itf.(*Group).Notify(job)
+}
+
+func (s *Subject) Groups(name string) *Group {
+ g, ok := s.groups.Get(name)
+ if !ok {
+ return nil
+ }
+ return g.(*Group)
+}
+
+func (s *Subject) GetOrNewGroup(name string) *Group {
+ item, _ := s.groups.Fetch(name, func() (interface{}, error) {
+ return NewGroup(name), nil
+ })
+ return item.(*Group)
+}
+
+func (s *Subject) Remove(name string) {
+ s.groups.Remove(name)
+}
+
+func (s *Subject) Size() int {
+ return s.groups.Size()
+}
+
+func NewSubject(name string) *Subject {
+ return &Subject{
+ name: name,
+ groups: util.NewConcurrentMap(0),
+ }
+}
diff --git a/server/service/notification/subject_test.go b/server/service/notification/subject_test.go
new file mode 100644
index 00000000..293c500e
--- /dev/null
+++ b/server/service/notification/subject_test.go
@@ -0,0 +1,65 @@
+/*
+ * 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 notification
+
+import "testing"
+
+func TestSubject_Fetch(t *testing.T) {
+ s := NewSubject("s1")
+ if s.Name() != "s1" {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ g := s.GetOrNewGroup("g1")
+ if g == nil {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ if s.GetOrNewGroup(g.Name()) != g {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ o := s.GetOrNewGroup("g2")
+ if s.Groups("g2") != o {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ if s.Size() != 2 {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ s.Remove(o.Name())
+ if s.Groups("g2") != nil {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ if s.Size() != 1 {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ mock1 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g1")}
+ mock2 := &mockSubscriber{BaseSubscriber: NewSubscriber(INSTANCE, "s1", "g2")}
+ g.AddSubscriber(mock1)
+ job := &BaseNotifyJob{group: "g3"}
+ s.Notify(job)
+ if mock1.job != nil || mock2.job != nil {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ job.group = "g1"
+ s.Notify(job)
+ if mock1.job != job || mock2.job != nil {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+ job.group = ""
+ s.Notify(job)
+ if mock1.job != job && mock2.job != job {
+ t.Fatalf("TestSubject_Fetch failed")
+ }
+}
diff --git a/server/service/notification/subscriber.go b/server/service/notification/subscriber.go
index 9f0b4dff..ece70139 100644
--- a/server/service/notification/subscriber.go
+++ b/server/service/notification/subscriber.go
@@ -18,67 +18,54 @@ package notification
import (
"errors"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
)
type Subscriber interface {
- Err() error
- SetError(err error)
Id() string
Subject() string
+ Group() string
Type() NotifyType
Service() *NotifyService
SetService(*NotifyService)
+
+ Err() error
+ SetError(err error)
+
+ Close()
OnAccept()
// The event bus will callback this function, so it must be non-blocked.
OnMessage(job NotifyJob)
- Close()
}
type BaseSubscriber struct {
id string
subject string
+ group string
nType NotifyType
service *NotifyService
err error
}
-func (s *BaseSubscriber) Err() error {
- return s.err
-}
-
-func (s *BaseSubscriber) SetError(err error) {
- s.err = err
- // 触发清理job
- s.Service().AddJob(NewNotifyServiceHealthCheckJob(s))
-}
-
-func (s *BaseSubscriber) Id() string {
- return s.id
-}
-
-func (s *BaseSubscriber) Subject() string {
- return s.subject
-}
-
-func (s *BaseSubscriber) Type() NotifyType {
- return s.nType
-}
-
-func (s *BaseSubscriber) Service() *NotifyService {
- return s.service
-}
-
-func (s *BaseSubscriber) SetService(svc *NotifyService) {
- s.service = svc
-}
-
-func (s *BaseSubscriber) OnAccept() {
-}
-
+func (s *BaseSubscriber) Id() string { return s.id }
+func (s *BaseSubscriber) Subject() string { return s.subject }
+func (s *BaseSubscriber) Group() string { return s.group }
+func (s *BaseSubscriber) Type() NotifyType { return s.nType }
+func (s *BaseSubscriber) Service() *NotifyService { return s.service }
+func (s *BaseSubscriber) SetService(svc *NotifyService) { s.service = svc }
+func (s *BaseSubscriber) Err() error { return s.err }
+func (s *BaseSubscriber) SetError(err error) { s.err = err }
+func (s *BaseSubscriber) Close() {}
+func (s *BaseSubscriber) OnAccept() {}
func (s *BaseSubscriber) OnMessage(job NotifyJob) {
s.SetError(errors.New("do not call base notifier OnMessage method"))
}
-func (s *BaseSubscriber) Close() {
-
+func NewSubscriber(nType NotifyType, subject, group string) *BaseSubscriber {
+ return &BaseSubscriber{
+ id: util.GenerateUuid(),
+ group: group,
+ subject: subject,
+ nType: nType,
+ }
}
diff --git a/server/service/notification/websocket.go b/server/service/notification/websocket.go
index f53e641d..362d7e2b 100644
--- a/server/service/notification/websocket.go
+++ b/server/service/notification/websocket.go
@@ -28,221 +28,212 @@ import (
"time"
)
-type WebSocketHandler struct {
+type WebSocket struct {
ctx context.Context
+ ticker *time.Ticker
conn *websocket.Conn
watcher *ListWatcher
needPingWatcher bool
closed chan struct{}
- goroutine *util.GoRoutine
}
-func (wh *WebSocketHandler) Init() error {
+func (wh *WebSocket) Init() error {
+ wh.ticker = time.NewTicker(wh.Timeout())
+
remoteAddr := wh.conn.RemoteAddr().String()
+
+ // subscribe backend
if err := GetNotifyService().AddSubscriber(wh.watcher); err != nil {
err = fmt.Errorf("establish[%s] websocket watch failed: notify service error, %s",
remoteAddr, err.Error())
util.Logger().Errorf(nil, err.Error())
- err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
- if err != nil {
- util.Logger().Errorf(err, "establish[%s] websocket watch failed: write message failed.", remoteAddr)
+ werr := wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
+ if werr != nil {
+ util.Logger().Errorf(werr, "establish[%s] websocket watch failed: write message failed.", remoteAddr)
}
return err
}
- util.Logger().Debugf("start watching instance status, watcher[%s] %s %s",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+
+ // publish events
+ publisher.Accept(wh)
+
+ util.Logger().Debugf("start watching instance status, watcher[%s], subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
return nil
}
-func (wh *WebSocketHandler) Timeout() time.Duration {
- return GetNotifyService().Config.NotifyTimeout
+func (wh *WebSocket) Timeout() time.Duration {
+ return DEFAULT_SEND_TIMEOUT
}
-func (wh *WebSocketHandler) websocketHeartbeat(messageType int) error {
+func (wh *WebSocket) heartbeat(messageType int) error {
err := wh.conn.WriteControl(messageType, []byte{}, time.Now().Add(wh.Timeout()))
if err != nil {
messageTypeName := "Ping"
if messageType == websocket.PongMessage {
messageTypeName = "Pong"
}
- util.Logger().Errorf(err, "fail to send '%s' to watcher[%s] %s %s", messageTypeName,
- wh.conn.RemoteAddr(), wh.watcher.Subject(), wh.watcher.Id())
+ util.Logger().Errorf(err, "fail to send '%s' to watcher[%s], subject: %s, group: %s",
+ messageTypeName, wh.conn.RemoteAddr(), wh.watcher.Subject(), wh.watcher.Group())
//wh.watcher.SetError(err)
return err
}
return nil
}
-func (wh *WebSocketHandler) HandleWatchWebSocketControlMessage(ctx context.Context) {
+func (wh *WebSocket) HandleWatchWebSocketControlMessage() {
defer close(wh.closed)
remoteAddr := wh.conn.RemoteAddr().String()
// PING
wh.conn.SetPingHandler(func(message string) error {
if wh.needPingWatcher {
- util.Logger().Infof("received 'Ping' message '%s' from watcher[%s] %s %s, no longer send 'Ping' to it",
- message, remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+ util.Logger().Infof("received 'Ping' message '%s' from watcher[%s], no longer send 'Ping' to it, subject: %s, group: %s",
+ message, remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
}
wh.needPingWatcher = false
- return wh.websocketHeartbeat(websocket.PongMessage)
+ return wh.heartbeat(websocket.PongMessage)
})
// PONG
wh.conn.SetPongHandler(func(message string) error {
- util.Logger().Debugf("received 'Pong' message %s from watcher[%s] %s %s",
- message, remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
+ util.Logger().Debugf("received 'Pong' message '%s' from watcher[%s], subject: %s, group: %s",
+ message, remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
return nil
})
// CLOSE
wh.conn.SetCloseHandler(func(code int, text string) error {
- util.Logger().Warnf(nil, "watcher[%s] %s %s active closed", remoteAddr,
- wh.watcher.Subject(), wh.watcher.Id())
- return wh.Close(code, text)
+ util.Logger().Warnf(nil, "watcher[%s] active closed, subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+ return wh.sendClose(code, text)
})
for {
- select {
- case <-ctx.Done():
+ _, _, err := wh.conn.ReadMessage()
+ if err != nil {
+ wh.watcher.SetError(err)
return
- default:
- _, _, err := wh.conn.ReadMessage()
- if err != nil {
- wh.watcher.SetError(err)
- return
- }
}
}
}
-func (wh *WebSocketHandler) HandleWatchWebSocketJob() {
- wh.goroutine.Do(wh.HandleWatchWebSocketControlMessage)
-
+func (wh *WebSocket) sendClose(code int, text string) error {
remoteAddr := wh.conn.RemoteAddr().String()
- for {
- timer := time.NewTimer(wh.Timeout())
- select {
- case <-wh.closed:
- timer.Stop()
- return
- case <-wh.ctx.Done():
- timer.Stop()
+ var message []byte
+ if code != websocket.CloseNoStatusReceived {
+ message = websocket.FormatCloseMessage(code, text)
+ }
+ err := wh.conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(wh.Timeout()))
+ if err != nil {
+ util.Logger().Errorf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+ return err
+ }
+ return nil
+}
- util.Logger().Warnf(nil, "handle timed out, watcher[%s] %s %s", remoteAddr,
- wh.watcher.Subject(), wh.watcher.Id())
- return
- case <-timer.C:
- if wh.watcher.Err() != nil {
- return
- }
-
- domainProject := util.ParseDomainProject(wh.ctx)
- if !serviceUtil.ServiceExist(context.Background(), domainProject, wh.watcher.Id()) {
- err := fmt.Errorf("Service does not exit.")
- util.Logger().Warnf(err, "watcher[%s] %s %s exit", remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(err.Error()))
- if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- }
- return
- }
-
- if !wh.needPingWatcher {
- continue
- }
-
- util.Logger().Debugf("send 'Ping' message to watcher[%s] %s %s", remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- err := wh.websocketHeartbeat(websocket.PingMessage)
- if err != nil {
- return
- }
- case job := <-wh.watcher.Job:
- timer.Stop()
-
- if wh.watcher.Err() != nil {
- return
- }
-
- if job == nil {
- err := wh.conn.WriteMessage(websocket.TextMessage,
- util.StringToBytesWithNoCopy("watcher catch an err: server shutdown"))
- if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- return
- }
- util.Logger().Warnf(nil, "watcher[%s] %s %s catch an err: server shutdown",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- return
- }
-
- resp := job.Response
-
- providerFlag := fmt.Sprintf("%s/%s/%s", resp.Key.AppId, resp.Key.ServiceName, resp.Key.Version)
- if resp.Action != string(pb.EVT_EXPIRE) {
- providerFlag = fmt.Sprintf("%s/%s(%s)", resp.Instance.ServiceId, resp.Instance.InstanceId, providerFlag)
- }
- util.Logger().Infof("event[%s] is coming in, watcher[%s] %s %s, providers' info %s",
- resp.Action, remoteAddr, wh.watcher.Subject(), wh.watcher.Id(), providerFlag)
-
- resp.Response = nil
- data, err := json.Marshal(resp)
- if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: marshal output file error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- message := fmt.Sprintf("marshal output file error, %s", err.Error())
- err = wh.conn.WriteMessage(websocket.TextMessage, util.StringToBytesWithNoCopy(message))
- if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- }
- return
- }
- err = wh.conn.WriteMessage(websocket.TextMessage, data)
- if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write message error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- return
- }
+func (wh *WebSocket) Pick() interface{} {
+ if wh.watcher.Err() != nil {
+ return wh.watcher.Err()
+ }
+ select {
+ case t := <-wh.ticker.C:
+ return t
+ case j := <-wh.watcher.Job:
+ if j == nil {
+ return fmt.Errorf("server shutdown")
}
+ return j
+ default:
+ return nil
}
}
-func (wh *WebSocketHandler) Close(code int, text string) error {
- defer wh.goroutine.Close(true)
-
+func (wh *WebSocket) HandleWatchWebSocketJob(o interface{}) {
remoteAddr := wh.conn.RemoteAddr().String()
var message []byte
- if code != websocket.CloseNoStatusReceived {
- message = websocket.FormatCloseMessage(code, text)
+
+ switch o.(type) {
+ case error:
+ err := o.(error)
+ util.Logger().Warnf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+
+ message = util.StringToBytesWithNoCopy(fmt.Sprintf("watcher catch an err: %s", err.Error()))
+ case time.Time:
+ domainProject := util.ParseDomainProject(wh.ctx)
+ if !serviceUtil.ServiceExist(wh.ctx, domainProject, wh.watcher.Group()) {
+ err := fmt.Errorf("Service does not exit.")
+ wh.watcher.SetError(err)
+ message = util.StringToBytesWithNoCopy(err.Error())
+ break
+ }
+
+ if !wh.needPingWatcher {
+ return
+ }
+
+ util.Logger().Debugf("send 'Ping' message to watcher[%s], subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
+ err := wh.heartbeat(websocket.PingMessage)
+ if err != nil {
+ wh.watcher.SetError(err)
+ }
+ return
+ case *WatchJob:
+ job := o.(*WatchJob)
+ resp := job.Response
+
+ providerFlag := fmt.Sprintf("%s/%s/%s", resp.Key.AppId, resp.Key.ServiceName, resp.Key.Version)
+ if resp.Action != string(pb.EVT_EXPIRE) {
+ providerFlag = fmt.Sprintf("%s/%s(%s)", resp.Instance.ServiceId, resp.Instance.InstanceId, providerFlag)
+ }
+ util.Logger().Infof("event[%s] is coming in, watcher[%s] watch %s, subject: %s, group: %s",
+ resp.Action, remoteAddr, providerFlag, wh.watcher.Subject(), wh.watcher.Group())
+
+ resp.Response = nil
+ data, err := json.Marshal(resp)
+ if err != nil {
+ wh.watcher.SetError(err)
+ message = util.StringToBytesWithNoCopy(fmt.Sprintf("marshal output file error, %s", err.Error()))
+ break
+ }
+ message = data
+ default:
+ wh.watcher.SetError(fmt.Errorf("unknown input %v", o))
+ return
}
- err := wh.conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(wh.Timeout()))
+
+ select {
+ case <-wh.closed:
+ return
+ default:
+ }
+
+ err := wh.conn.WriteMessage(websocket.TextMessage, message)
if err != nil {
- util.Logger().Errorf(err, "watcher[%s] %s %s catch an err: write 'Close' message error",
- remoteAddr, wh.watcher.Subject(), wh.watcher.Id())
- return err
+ util.Logger().Errorf(err, "watcher[%s] catch an err, subject: %s, group: %s",
+ remoteAddr, wh.watcher.Subject(), wh.watcher.Group())
}
- return nil
}
func DoWebSocketListAndWatch(ctx context.Context, serviceId string, f func() ([]*pb.WatchInstanceResponse, int64), conn *websocket.Conn) {
domainProject := util.ParseDomainProject(ctx)
- handler := &WebSocketHandler{
+ socket := &WebSocket{
ctx: ctx,
conn: conn,
watcher: NewListWatcher(serviceId, apt.GetInstanceRootKey(domainProject)+"/", f),
needPingWatcher: true,
closed: make(chan struct{}),
- goroutine: util.NewGo(context.Background()),
}
- processHandler(handler)
+ process(socket)
}
-func processHandler(handler *WebSocketHandler) {
- if err := handler.Init(); err != nil {
+func process(socket *WebSocket) {
+ if err := socket.Init(); err != nil {
return
}
- handler.HandleWatchWebSocketJob()
+ socket.HandleWatchWebSocketControlMessage()
}
func EstablishWebSocketError(conn *websocket.Conn, err error) {
diff --git a/server/service/notification/websocket_test.go b/server/service/notification/websocket_test.go
new file mode 100644
index 00000000..5b0a9f47
--- /dev/null
+++ b/server/service/notification/websocket_test.go
@@ -0,0 +1,120 @@
+/*
+ * 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 notification
+
+import (
+ "context"
+ "errors"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ _ "github.com/apache/incubator-servicecomb-service-center/server/plugin/infra/registry/buildin"
+ "github.com/gorilla/websocket"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+ "testing"
+ "time"
+)
+
+var closeCh = make(chan struct{})
+
+type watcherConn struct {
+}
+
+func (h *watcherConn) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+ var upgrader = websocket.Upgrader{}
+ conn, _ := upgrader.Upgrade(w, r, nil)
+ for {
+ conn.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(time.Second))
+ conn.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))
+ _, _, err := conn.ReadMessage()
+ if err != nil {
+ return
+ }
+ <-closeCh
+ conn.WriteControl(websocket.CloseMessage, []byte{}, time.Now().Add(time.Second))
+ conn.Close()
+ return
+ }
+}
+
+func TestDoWebSocketListAndWatch(t *testing.T) {
+ s := httptest.NewServer(&watcherConn{})
+
+ GetNotifyService().Start()
+
+ conn, _, _ := websocket.DefaultDialer.Dial(
+ strings.Replace(s.URL, "http://", "ws://", 1), nil)
+
+ go DoWebSocketListAndWatch(context.Background(), "", nil, conn)
+
+ EstablishWebSocketError(conn, errors.New("error"))
+
+ w := NewListWatcher("g", "s", func() (results []*proto.WatchInstanceResponse, rev int64) {
+ return
+ })
+ w.nType = -1
+
+ ws := &WebSocket{
+ ctx: context.Background(),
+ conn: conn,
+ watcher: w,
+ needPingWatcher: true,
+ closed: make(chan struct{}),
+ }
+ err := ws.Init()
+ if err == nil {
+ t.Fatalf("TestPublisher_Run")
+ }
+
+ w.nType = INSTANCE
+ err = ws.Init()
+ if err != nil {
+ t.Fatalf("TestPublisher_Run")
+ }
+ go ws.HandleWatchWebSocketControlMessage()
+
+ w.SetError(errors.New("err"))
+ w.OnMessage(nil)
+ w.SetError(nil)
+ w.OnMessage(nil)
+ w.OnMessage(&WatchJob{})
+
+ GetNotifyService().AddJob(NewWatchJob("g", "s", 1, &proto.WatchInstanceResponse{
+ Response: proto.CreateResponse(proto.Response_SUCCESS, "ok"),
+ Action: string(proto.EVT_CREATE),
+ Key: &proto.MicroServiceKey{},
+ Instance: &proto.MicroServiceInstance{},
+ }))
+
+ <-time.After(time.Second)
+
+ ws.HandleWatchWebSocketJob(nil)
+
+ ws.heartbeat(websocket.PingMessage)
+ ws.heartbeat(websocket.PongMessage)
+
+ ws.HandleWatchWebSocketJob(time.Now())
+
+ closeCh <- struct{}{}
+
+ <-time.After(time.Second)
+
+ ws.heartbeat(websocket.PingMessage)
+ ws.heartbeat(websocket.PongMessage)
+
+ publisher.Stop()
+}
diff --git a/server/service/schema.go b/server/service/schema.go
index b59a38dd..17b1f08c 100644
--- a/server/service/schema.go
+++ b/server/service/schema.go
@@ -75,7 +75,7 @@ func (s *MicroServiceService) GetSchemaInfo(ctx context.Context, in *pb.GetSchem
return &pb.GetSchemaResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "Get schema info successfully."),
- Schema: util.BytesToStringWithNoCopy(resp.Kvs[0].Value),
+ Schema: util.BytesToStringWithNoCopy(resp.Kvs[0].Value.([]byte)),
SchemaSummary: schemaSummary,
}, nil
}
@@ -123,7 +123,7 @@ func (s *MicroServiceService) GetAllSchemaInfo(ctx context.Context, in *pb.GetAl
}, errDo
}
- respWithSchema := ®istry.PluginResponse{}
+ respWithSchema := &backend.Response{}
if in.WithSchema {
key := apt.GenerateServiceSchemaKey(domainProject, in.ServiceId, "")
opts := append(serviceUtil.FromContext(ctx), registry.WithStrKey(key), registry.WithPrefix())
@@ -141,16 +141,16 @@ func (s *MicroServiceService) GetAllSchemaInfo(ctx context.Context, in *pb.GetAl
tempSchema := &pb.Schema{}
tempSchema.SchemaId = schemaId
for _, summarySchema := range resp.Kvs {
- schemaIdOfSummary, summaryData := pb.GetInfoFromSchemaSummaryKV(summarySchema)
+ schemaIdOfSummary := backend.GetInfoFromSchemaSummaryKV(summarySchema)
if schemaId == schemaIdOfSummary {
- tempSchema.Summary = util.BytesToStringWithNoCopy(summaryData)
+ tempSchema.Summary = summarySchema.Value.(string)
}
}
for _, contentSchema := range respWithSchema.Kvs {
- schemaIdOfSchema, schemaData := pb.GetInfoFromSchemaKV(contentSchema)
+ schemaIdOfSchema := backend.GetInfoFromSchemaKV(contentSchema)
if schemaId == schemaIdOfSchema {
- tempSchema.Schema = util.BytesToStringWithNoCopy(schemaData)
+ tempSchema.Schema = util.BytesToStringWithNoCopy(contentSchema.Value.([]byte))
}
}
schemas = append(schemas, tempSchema)
@@ -467,7 +467,7 @@ func GetSchemasFromDatabase(ctx context.Context, domainProject string, serviceId
key := util.BytesToStringWithNoCopy(kv.Key)
tmp := strings.Split(key, "/")
schemaId := tmp[len(tmp)-1]
- schema := util.BytesToStringWithNoCopy(kv.Value)
+ schema := util.BytesToStringWithNoCopy(kv.Value.([]byte))
schemaStruct := &pb.Schema{
SchemaId: schemaId,
Schema: schema,
@@ -672,5 +672,5 @@ func getSchemaSummary(ctx context.Context, domainProject string, serviceId strin
if len(resp.Kvs) == 0 {
return "", nil
}
- return util.BytesToStringWithNoCopy(resp.Kvs[0].Value), nil
+ return resp.Kvs[0].Value.(string), nil
}
diff --git a/server/service/tag_test.go b/server/service/tag_test.go
index cee87994..1ca9112d 100644
--- a/server/service/tag_test.go
+++ b/server/service/tag_test.go
@@ -375,8 +375,7 @@ var _ = Describe("'Tag' service", func() {
VersionRule: "1.0.0+",
Tags: []string{"not-exist-tag"},
})
- Expect(findResp.Response.Code).To(Equal(pb.Response_SUCCESS))
- Expect(len(findResp.Instances)).To(Equal(0))
+ Expect(findResp.Response.Code).To(Equal(scerr.ErrServiceNotExists))
findResp, err = instanceResource.Find(getContext(), &pb.FindInstancesRequest{
ConsumerServiceId: consumerId,
@@ -394,7 +393,7 @@ var _ = Describe("'Tag' service", func() {
Rules: []*pb.AddOrUpdateServiceRule{
&pb.AddOrUpdateServiceRule{
RuleType: "WHITE",
- Attribute: "tag_filter_tag",
+ Attribute: "tag_consumer_tag",
Pattern: "f*",
Description: "test white",
},
@@ -410,12 +409,11 @@ var _ = Describe("'Tag' service", func() {
VersionRule: "1.0.0+",
Tags: []string{"filter_tag"},
})
- Expect(findResp.Response.Code).To(Equal(pb.Response_SUCCESS))
- Expect(len(findResp.Instances)).To(Equal(0))
+ Expect(findResp.Response.Code).To(Equal(scerr.ErrServiceNotExists))
addTagResp, err = serviceResource.AddTags(getContext(), &pb.AddServiceTagsRequest{
ServiceId: consumerId,
- Tags: map[string]string{"filter_tag": "filter"},
+ Tags: map[string]string{"consumer_tag": "filter"},
})
Expect(err).To(BeNil())
Expect(addTagResp.Response.Code).To(Equal(pb.Response_SUCCESS))
diff --git a/server/service/util/common.go b/server/service/util/common.go
index 9e31b3f7..6c25e513 100644
--- a/server/service/util/common.go
+++ b/server/service/util/common.go
@@ -16,14 +16,10 @@
*/
package util
-import "time"
-
const (
HEADER_REV = "X-Resource-Revision"
CTX_NOCACHE = "noCache"
CTX_CACHEONLY = "cacheOnly"
CTX_REQUEST_REVISION = "requestRev"
CTX_RESPONSE_REVISION = "responseRev"
-
- cacheTTL = 5 * time.Minute
)
diff --git a/server/service/util/dependency.go b/server/service/util/dependency.go
index 4a5ed1b3..f26f0f7b 100644
--- a/server/service/util/dependency.go
+++ b/server/service/util/dependency.go
@@ -19,532 +19,14 @@ package util
import (
"encoding/json"
"errors"
- "fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
apt "github.com/apache/incubator-servicecomb-service-center/server/core"
"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
"golang.org/x/net/context"
- "strings"
)
-func GetConsumersInCache(ctx context.Context, domainProject string, provider *pb.MicroService) ([]string, error) {
- // 查询所有consumer
- dr := NewProviderDependencyRelation(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
- domainProject, provider)
- consumerIds, err := dr.GetDependencyConsumerIds()
- if err != nil {
- util.Logger().Errorf(err, "Get dependency consumerIds failed.%s", provider.ServiceId)
- return nil, err
- }
- return consumerIds, nil
-}
-
-func GetProvidersInCache(ctx context.Context, domainProject string, consumer *pb.MicroService) ([]string, error) {
- // 查询所有provider
- dr := NewConsumerDependencyRelation(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
- domainProject, consumer)
- providerIds, err := dr.GetDependencyProviderIds()
- if err != nil {
- util.Logger().Errorf(err, "Get dependency providerIds failed.%s", consumer.ServiceId)
- return nil, err
- }
- return providerIds, nil
-}
-
-func GetConsumerIdsByProvider(ctx context.Context, domainProject string, provider *pb.MicroService) (allow []string, deny []string, _ error) {
- if provider == nil || len(provider.ServiceId) == 0 {
- return nil, nil, fmt.Errorf("invalid provider")
- }
-
- //todo 删除服务,最后实例推送有误差
- providerRules, err := GetRulesUtil(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"),
- domainProject, provider.ServiceId)
- if err != nil {
- return nil, nil, err
- }
- if len(providerRules) == 0 {
- return getConsumerIdsWithFilter(ctx, domainProject, provider, noFilter)
- }
-
- rf := RuleFilter{
- DomainProject: domainProject,
- Provider: provider,
- ProviderRules: providerRules,
- }
-
- allow, deny, err = getConsumerIdsWithFilter(ctx, domainProject, provider, rf.Filter)
- if err != nil {
- return nil, nil, err
- }
- return allow, deny, nil
-}
-
-func getConsumerIdsWithFilter(ctx context.Context, domainProject string, provider *pb.MicroService,
- filter func(ctx context.Context, consumerId string) (bool, error)) (allow []string, deny []string, err error) {
- consumerIds, err := GetConsumersInCache(ctx, domainProject, provider)
- if err != nil {
- return nil, nil, err
- }
- return filterConsumerIds(ctx, consumerIds, filter)
-}
-
-func filterConsumerIds(ctx context.Context, consumerIds []string,
- filter func(ctx context.Context, consumerId string) (bool, error)) (allow []string, deny []string, err error) {
- l := len(consumerIds)
- if l == 0 {
- return nil, nil, nil
- }
- allowIdx, denyIdx := 0, l
- consumers := make([]string, l)
- for _, consumerId := range consumerIds {
- ok, err := filter(ctx, consumerId)
- if err != nil {
- return nil, nil, err
- }
- if ok {
- consumers[allowIdx] = consumerId
- allowIdx++
- } else {
- denyIdx--
- consumers[denyIdx] = consumerId
- }
- }
- return consumers[:allowIdx], consumers[denyIdx:], nil
-}
-
-func noFilter(_ context.Context, _ string) (bool, error) {
- return true, nil
-}
-
-func GetProviderIdsByConsumer(ctx context.Context, domainProject string, service *pb.MicroService) (allow []string, deny []string, _ error) {
- providerIdsInCache, err := GetProvidersInCache(ctx, domainProject, service)
- if err != nil {
- return nil, nil, err
- }
- l := len(providerIdsInCache)
- rf := RuleFilter{
- DomainProject: domainProject,
- }
- allowIdx, denyIdx := 0, l
- providerIds := make([]string, l)
- copyCtx := util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1")
- for _, providerId := range providerIdsInCache {
- provider, err := GetService(ctx, domainProject, providerId)
- if provider == nil {
- continue
- }
- providerRules, err := GetRulesUtil(copyCtx, domainProject, provider.ServiceId)
- if err != nil {
- return nil, nil, err
- }
- if len(providerRules) == 0 {
- providerIds[allowIdx] = providerId
- allowIdx++
- continue
- }
- rf.Provider = provider
- rf.ProviderRules = providerRules
- ok, err := rf.Filter(ctx, service.ServiceId)
- if err != nil {
- return nil, nil, err
- }
- if ok {
- providerIds[allowIdx] = providerId
- allowIdx++
- } else {
- denyIdx--
- providerIds[denyIdx] = providerId
- }
- }
- return providerIds[:allowIdx], providerIds[denyIdx:], nil
-}
-
-func DependencyRuleExist(ctx context.Context, provider *pb.MicroServiceKey, consumer *pb.MicroServiceKey) (bool, error) {
- targetDomainProject := provider.Tenant
- if len(targetDomainProject) == 0 {
- targetDomainProject = consumer.Tenant
- }
-
- consumerKey := apt.GenerateConsumerDependencyRuleKey(consumer.Tenant, consumer)
- existed, err := dependencyRuleExistUtil(ctx, consumerKey, provider)
- if err != nil || existed {
- return existed, err
- }
-
- providerKey := apt.GenerateProviderDependencyRuleKey(targetDomainProject, provider)
- return dependencyRuleExistUtil(ctx, providerKey, consumer)
-}
-
-func dependencyRuleExistUtil(ctx context.Context, key string, target *pb.MicroServiceKey) (bool, error) {
- compareData, err := TransferToMicroServiceDependency(ctx, key)
- if err != nil {
- return false, err
- }
- if len(compareData.Dependency) != 0 {
- isEqual, err := containServiceDependency(compareData.Dependency, target)
- if err != nil {
- return false, err
- }
- if isEqual {
- //删除之前的依赖
- return true, nil
- }
- }
- return false, nil
-}
-
-func AddServiceVersionRule(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroServiceKey) error {
- //创建依赖一致
- consumerKey := pb.MicroServiceToKey(domainProject, consumer)
- exist, err := DependencyRuleExist(ctx, provider, consumerKey)
- if exist || err != nil {
- return err
- }
-
- r := &pb.ConsumerDependency{
- Consumer: consumerKey,
- Providers: []*pb.MicroServiceKey{provider},
- Override: false,
- }
- data, err := json.Marshal(r)
- if err != nil {
- return err
- }
-
- id := util.StringJoin([]string{provider.AppId, provider.ServiceName}, "_")
- key := apt.GenerateConsumerDependencyQueueKey(domainProject, consumer.ServiceId, id)
- resp, err := backend.Registry().TxnWithCmp(ctx,
- nil,
- []registry.CompareOp{registry.OpCmp(registry.CmpStrVal(key), registry.CMP_EQUAL, util.BytesToStringWithNoCopy(data))},
- []registry.PluginOp{registry.OpPut(registry.WithStrKey(key), registry.WithValue(data))})
- if err != nil {
- return err
- }
- if !resp.Succeeded {
- util.Logger().Infof("find request into dependency queue successfully, %s: %v", key, r)
- }
- return nil
-}
-
-func TransferToMicroServiceDependency(ctx context.Context, key string) (*pb.MicroServiceDependency, error) {
- microServiceDependency := &pb.MicroServiceDependency{
- Dependency: []*pb.MicroServiceKey{},
- }
-
- opts := append(FromContext(ctx), registry.WithStrKey(key))
- res, err := backend.Store().DependencyRule().Search(ctx, opts...)
- if err != nil {
- util.Logger().Errorf(nil, "Get dependency rule failed.")
- return nil, err
- }
- if len(res.Kvs) != 0 {
- err = json.Unmarshal(res.Kvs[0].Value, microServiceDependency)
- if err != nil {
- util.Logger().Errorf(nil, "Unmarshal res failed.")
- return nil, err
- }
- }
- return microServiceDependency, nil
-}
-
-func updateProviderDependencyRuleUtil(consumersOfProvideRules *pb.MicroServiceDependency, consumer *pb.MicroServiceKey, providerRuleKey string) (registry.PluginOp, error) {
- for key, consumerInner := range consumersOfProvideRules.Dependency {
- if ok := equalServiceDependency(consumerInner, consumer); ok {
- consumersOfProvideRules.Dependency = append(consumersOfProvideRules.Dependency[:key], consumersOfProvideRules.Dependency[key+1:]...)
- break
- }
- }
- if len(consumersOfProvideRules.Dependency) == 0 {
- util.Logger().Infof("delete dependency rule key is %s", providerRuleKey)
- return registry.OpDel(registry.WithStrKey(providerRuleKey)), nil
- } else {
- data, err := json.Marshal(consumersOfProvideRules)
- if err != nil {
- util.Logger().Errorf(nil, "update dependency key Marshal tmpValue failed.")
- return registry.PluginOp{}, err
- }
- util.Logger().Infof("put provider's dependency rule, key is %s, value is %v.", providerRuleKey, consumersOfProvideRules)
- return registry.OpPut(registry.WithStrKey(providerRuleKey), registry.WithValue(data)), nil
- }
-}
-
-func equalServiceDependency(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
- stringA := toString(serviceA)
- stringB := toString(serviceB)
- if stringA == stringB {
- return true
- }
- return false
-}
-
-func diffServiceVersion(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
- stringA := toString(serviceA)
- stringB := toString(serviceB)
- if stringA != stringB &&
- stringA[:strings.LastIndex(stringA, "/")+1] == stringB[:strings.LastIndex(stringB, "/")+1] {
- return true
- }
- return false
-}
-
-func toString(in *pb.MicroServiceKey) string {
- return apt.GenerateProviderDependencyRuleKey(in.Tenant, in)
-}
-
-func parseAddOrUpdateRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
- conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
-
- oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
- if err != nil {
- util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
- dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
- return
- }
-
- deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
- newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
- existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
- for _, tmpProviderRule := range dep.ProvidersRule {
- if ok, _ := containServiceDependency(oldProviderRules.Dependency, tmpProviderRule); ok {
- continue
- }
-
- if tmpProviderRule.ServiceName == "*" {
- newDependencyRuleList = append([]*pb.MicroServiceKey{}, tmpProviderRule)
- deleteDependencyRuleList = oldProviderRules.Dependency
- break
- }
-
- newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
- old := isNeedUpdate(oldProviderRules.Dependency, tmpProviderRule)
- if old != nil {
- deleteDependencyRuleList = append(deleteDependencyRuleList, old)
- }
- }
- for _, oldProviderRule := range oldProviderRules.Dependency {
- if oldProviderRule.ServiceName == "*" {
- newDependencyRuleList = nil
- deleteDependencyRuleList = nil
- return
- }
- if ok, _ := containServiceDependency(deleteDependencyRuleList, oldProviderRule); !ok {
- existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
- }
- }
-
- dep.ProvidersRule = append(newDependencyRuleList, existDependencyRuleList...)
- return
-}
-
-func parseOverrideRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
- conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
-
- oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
- if err != nil {
- util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
- dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
- return
- }
-
- deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
- newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
- existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
- for _, oldProviderRule := range oldProviderRules.Dependency {
- if ok, _ := containServiceDependency(dep.ProvidersRule, oldProviderRule); !ok {
- deleteDependencyRuleList = append(deleteDependencyRuleList, oldProviderRule)
- } else {
- existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
- }
- }
- for _, tmpProviderRule := range dep.ProvidersRule {
- if ok, _ := containServiceDependency(existDependencyRuleList, tmpProviderRule); !ok {
- newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
- }
- }
- return
-}
-
-func syncDependencyRule(ctx context.Context, dep *Dependency, filter func(context.Context, *Dependency) (_, _, _ []*pb.MicroServiceKey)) error {
- //更新consumer的providers的值,consumer的版本是确定的
- consumerFlag := strings.Join([]string{dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version}, "/")
-
- newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList := filter(ctx, dep)
- if len(newDependencyRuleList) == 0 && len(existDependencyRuleList) == 0 && len(deleteDependencyRuleList) == 0 {
- return nil
- }
-
- dep.err = make(chan error, 5)
- dep.chanNum = 0
- if len(deleteDependencyRuleList) != 0 {
- util.Logger().Infof("Delete dependency rule remove for consumer %s, %v, ", consumerFlag, deleteDependencyRuleList)
- dep.removedDependencyRuleList = deleteDependencyRuleList
- dep.RemoveConsumerOfProviderRule()
- }
-
- if len(newDependencyRuleList) != 0 {
- util.Logger().Infof("New dependency rule add for consumer %s, %v, ", consumerFlag, newDependencyRuleList)
- dep.NewDependencyRuleList = newDependencyRuleList
- dep.AddConsumerOfProviderRule()
- }
-
- conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
- err := dep.UpdateProvidersRuleOfConsumer(conKey)
- if err != nil {
- return err
- }
-
- if dep.chanNum != 0 {
- for tmpErr := range dep.err {
- dep.chanNum--
- if tmpErr != nil {
- return tmpErr
- }
- if 0 == dep.chanNum {
- close(dep.err)
- }
- }
- }
- return nil
-}
-
-func AddDependencyRule(ctx context.Context, dep *Dependency) error {
- return syncDependencyRule(ctx, dep, parseAddOrUpdateRules)
-}
-
-func CreateDependencyRule(ctx context.Context, dep *Dependency) error {
- return syncDependencyRule(ctx, dep, parseOverrideRules)
-}
-
-func isDependencyAll(dep *pb.MicroServiceDependency) bool {
- for _, serviceDep := range dep.Dependency {
- if serviceDep.ServiceName == "*" {
- return true
- }
- }
- return false
-}
-
-func isExist(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) bool {
- for _, tmp := range services {
- if equalServiceDependency(tmp, service) {
- return true
- }
- }
- return false
-}
-
-func deleteConsumerDepOfProviderRule(ctx context.Context, domainProject string, providerRule *pb.MicroServiceKey, deleteConsumer *pb.MicroServiceKey) (registry.PluginOp, error) {
- proKey := apt.GenerateProviderDependencyRuleKey(domainProject, providerRule)
- consumerDepRules, err := TransferToMicroServiceDependency(ctx, proKey)
- if err != nil {
- return registry.PluginOp{}, err
- }
- return deleteDepRuleUtil(proKey, consumerDepRules, deleteConsumer)
-}
-
-func deleteDepRuleUtil(key string, deps *pb.MicroServiceDependency, deleteDepRule *pb.MicroServiceKey) (registry.PluginOp, error) {
- for key, consumerDepRule := range deps.Dependency {
- if equalServiceDependency(consumerDepRule, deleteDepRule) {
- deps.Dependency = append(deps.Dependency[:key], deps.Dependency[key+1:]...)
- break
- }
- }
- data, err := json.Marshal(deps)
- if err != nil {
- return registry.PluginOp{}, err
- }
- return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func addDepRuleUtil(key string, deps *pb.MicroServiceDependency, updateDepRule *pb.MicroServiceKey) (registry.PluginOp, error) {
- deps.Dependency = append(deps.Dependency, updateDepRule)
- data, err := json.Marshal(deps)
- if err != nil {
- util.Logger().Errorf(err, "marshal consumerDepRules failed for delete consumer rule from provider rule's dep.")
- return registry.PluginOp{}, err
- }
- return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func updateDepRuleUtil(key string, deps *pb.MicroServiceDependency, updateDepRule *pb.MicroServiceKey) (string, registry.PluginOp, error) {
- oldRule := ""
- for _, serviceRule := range deps.Dependency {
- if diffServiceVersion(serviceRule, updateDepRule) {
- oldRule = serviceRule.Version
- serviceRule.Version = updateDepRule.Version
- break
- }
- }
- data, err := json.Marshal(deps)
- if err != nil {
- return oldRule, registry.PluginOp{}, err
- }
- return oldRule, registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-func isNeedUpdate(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) *pb.MicroServiceKey {
- for _, tmp := range services {
- if diffServiceVersion(tmp, service) {
- return tmp
- }
- }
- return nil
-}
-
-func containServiceDependency(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) (bool, error) {
- if services == nil || service == nil {
- return false, errors.New("Invalid params input.")
- }
- for _, value := range services {
- rst := equalServiceDependency(service, value)
- if rst {
- return true, nil
- }
- }
- return false, nil
-}
-
-func BadParamsResponse(detailErr string) *pb.CreateDependenciesResponse {
- util.Logger().Errorf(nil, "Request params is invalid. %s", detailErr)
- if len(detailErr) == 0 {
- detailErr = "Request params is invalid."
- }
- return &pb.CreateDependenciesResponse{
- Response: pb.CreateResponse(scerr.ErrInvalidParams, detailErr),
- }
-}
-
-func ParamsChecker(consumerInfo *pb.MicroServiceKey, providersInfo []*pb.MicroServiceKey) *pb.CreateDependenciesResponse {
- flag := make(map[string]bool, len(providersInfo))
- for _, providerInfo := range providersInfo {
- //存在带*的情况,后面的数据就不校验了
- if providerInfo.ServiceName == "*" {
- util.Logger().Debugf("%s 's provider contains *.", consumerInfo.ServiceName)
- break
- }
- if len(providerInfo.AppId) == 0 {
- providerInfo.AppId = consumerInfo.AppId
- }
-
- version := providerInfo.Version
- if len(version) == 0 {
- return BadParamsResponse("Required provider version")
- }
-
- providerInfo.Version = ""
- if _, ok := flag[toString(providerInfo)]; ok {
- return BadParamsResponse("Invalid request body for provider info.Duplicate provider or (serviceName and appId is same).")
- } else {
- flag[toString(providerInfo)] = true
- }
- providerInfo.Version = version
- }
- return nil
-}
-
type Dependency struct {
ConsumerId string
DomainProject string
@@ -673,352 +155,3 @@ func (dep *Dependency) UpdateProvidersRuleOfConsumer(conKey string) error {
}
return nil
}
-
-type DependencyRelation struct {
- ctx context.Context
- domainProject string
- consumer *pb.MicroService
- provider *pb.MicroService
-}
-
-func NewProviderDependencyRelation(ctx context.Context, domainProject string, provider *pb.MicroService) *DependencyRelation {
- return NewDependencyRelation(ctx, domainProject, nil, provider)
-}
-
-func NewConsumerDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService) *DependencyRelation {
- return NewDependencyRelation(ctx, domainProject, consumer, nil)
-}
-
-func NewDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroService) *DependencyRelation {
- return &DependencyRelation{
- ctx: ctx,
- domainProject: domainProject,
- consumer: consumer,
- provider: provider,
- }
-}
-
-func (dr *DependencyRelation) GetDependencyProviders(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
- keys, err := dr.getProviderKeys()
- if err != nil {
- return nil, err
- }
- services := make([]*pb.MicroService, 0, len(keys))
- op := toDependencyRelationFilterOpt(opts...)
- for _, key := range keys {
- if op.SameDomainProject && key.Tenant != dr.domainProject {
- continue
- }
-
- providerIds, err := dr.parseDependencyRule(key)
- if err != nil {
- return nil, err
- }
-
- if key.ServiceName == "*" {
- services = services[:0]
- }
-
- for _, providerId := range providerIds {
- provider, err := GetService(dr.ctx, key.Tenant, providerId)
- if err != nil {
- util.Logger().Warnf(nil, "Provider does not exist, %s/%s/%s",
- key.AppId, key.ServiceName, key.Version)
- continue
- }
- if provider == nil {
- continue
- }
- if op.NonSelf && providerId == dr.consumer.ServiceId {
- continue
- }
- services = append(services, provider)
- }
-
- if key.ServiceName == "*" {
- break
- }
- }
- return services, nil
-}
-
-func (dr *DependencyRelation) GetDependencyProviderIds() ([]string, error) {
- keys, err := dr.getProviderKeys()
- if err != nil {
- return nil, err
- }
- return dr.getDependencyProviderIds(keys)
-}
-
-func (dr *DependencyRelation) getProviderKeys() ([]*pb.MicroServiceKey, error) {
- if dr.consumer == nil {
- util.LOGGER.Infof("dr.consumer is nil ------->")
- return nil, fmt.Errorf("Invalid consumer")
- }
- consumerMicroServiceKey := pb.MicroServiceToKey(dr.domainProject, dr.consumer)
-
- conKey := apt.GenerateConsumerDependencyRuleKey(dr.domainProject, consumerMicroServiceKey)
- consumerDependency, err := TransferToMicroServiceDependency(dr.ctx, conKey)
- if err != nil {
- return nil, err
- }
- return consumerDependency.Dependency, nil
-}
-
-func (dr *DependencyRelation) getDependencyProviderIds(providerRules []*pb.MicroServiceKey) ([]string, error) {
- provideServiceIds := make([]string, 0, len(providerRules))
- for _, provider := range providerRules {
- serviceIds, err := dr.parseDependencyRule(provider)
- switch {
- case provider.ServiceName == "*":
- if err != nil {
- util.Logger().Errorf(err, "Add dependency failed, rely all service: get all services failed.")
- return provideServiceIds, err
- }
- return serviceIds, nil
- default:
- if err != nil {
- util.Logger().Errorf(err, "Get providerIds failed, service: %s/%s/%s",
- provider.AppId, provider.ServiceName, provider.Version)
- return provideServiceIds, err
- }
- if len(serviceIds) == 0 {
- util.Logger().Warnf(nil, "Get providerIds is empty, service: %s/%s/%s does not exist",
- provider.AppId, provider.ServiceName, provider.Version)
- continue
- }
- provideServiceIds = append(provideServiceIds, serviceIds...)
- }
- }
- return provideServiceIds, nil
-}
-
-func (dr *DependencyRelation) parseDependencyRule(dependencyRule *pb.MicroServiceKey) (serviceIds []string, err error) {
- opts := FromContext(dr.ctx)
- switch {
- case dependencyRule.ServiceName == "*":
- util.Logger().Infof("Rely all service, * type, consumerId %s", dr.consumer.ServiceId)
- splited := strings.Split(apt.GenerateServiceIndexKey(dependencyRule), "/")
- allServiceKey := util.StringJoin(splited[:len(splited)-3], "/") + "/"
- sopts := append(opts,
- registry.WithStrKey(allServiceKey),
- registry.WithPrefix())
- resp, err := backend.Store().Service().Search(dr.ctx, sopts...)
- if err != nil {
- return nil, err
- }
-
- for _, kv := range resp.Kvs {
- serviceIds = append(serviceIds, util.BytesToStringWithNoCopy(kv.Value))
- }
- default:
- serviceIds, err = FindServiceIds(dr.ctx, dependencyRule.Version, dependencyRule)
- }
- return
-}
-
-func (dr *DependencyRelation) GetDependencyConsumers(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
- consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
- if err != nil {
- util.Logger().Errorf(err, "Get consumers of provider rule failed, %s", dr.provider.ServiceId)
- return nil, err
- }
- consumers := make([]*pb.MicroService, 0)
- op := toDependencyRelationFilterOpt(opts...)
- for _, consumer := range consumerDependAllList {
- if op.SameDomainProject && consumer.Tenant != dr.domainProject {
- continue
- }
-
- service, err := dr.getServiceByMicroServiceKey(consumer)
- if err != nil {
- return nil, err
- }
- if service == nil {
- util.Logger().Warnf(nil, "Consumer does not exist, %v", consumer)
- continue
- }
-
- if op.NonSelf && service.ServiceId == dr.provider.ServiceId {
- continue
- }
-
- consumers = append(consumers, service)
- }
- return consumers, nil
-}
-
-func (dr *DependencyRelation) getServiceByMicroServiceKey(service *pb.MicroServiceKey) (*pb.MicroService, error) {
- serviceId, err := GetServiceId(dr.ctx, service)
- if err != nil {
- return nil, err
- }
- if len(serviceId) == 0 {
- util.Logger().Warnf(nil, "Service not exist,%v", service)
- return nil, nil
- }
- return GetService(dr.ctx, service.Tenant, serviceId)
-}
-
-func (dr *DependencyRelation) GetDependencyConsumerIds() ([]string, error) {
- consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
- if err != nil {
- return nil, err
- }
- consumerIds := make([]string, 0, len(consumerDependAllList))
- for _, consumer := range consumerDependAllList {
- consumerId, err := GetServiceId(dr.ctx, consumer)
- if err != nil {
- util.Logger().Errorf(err, "Get consumer failed, %v", consumer)
- return nil, err
- }
- if len(consumerId) == 0 {
- util.Logger().Warnf(nil, "Get consumer not exist, %v", consumer)
- continue
- }
- consumerIds = append(consumerIds, consumerId)
- }
- return consumerIds, nil
-
-}
-
-func (dr *DependencyRelation) getDependencyConsumersOfProvider() ([]*pb.MicroServiceKey, error) {
- if dr.provider == nil {
- util.LOGGER.Infof("dr.provider is nil ------->")
- return nil, fmt.Errorf("Invalid provider")
- }
- providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
- consumerDependAllList, err := dr.getConsumerOfDependAllServices()
- if err != nil {
- util.Logger().Errorf(err, "Get consumer that depend on all services failed, %s", dr.provider.ServiceId)
- return nil, err
- }
-
- consumerDependList, err := dr.getConsumerOfSameServiceNameAndAppId(providerService)
- if err != nil {
- util.Logger().Errorf(err, "Get consumer that depend on same serviceName and appid rule failed, %s",
- dr.provider.ServiceId)
- return nil, err
- }
- consumerDependAllList = append(consumerDependAllList, consumerDependList...)
- return consumerDependAllList, nil
-}
-
-func (dr *DependencyRelation) getConsumerOfDependAllServices() ([]*pb.MicroServiceKey, error) {
- providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
- providerService.ServiceName = "*"
- relyAllKey := apt.GenerateProviderDependencyRuleKey(dr.domainProject, providerService)
- opts := append(FromContext(dr.ctx), registry.WithStrKey(relyAllKey))
- rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
- if err != nil {
- util.Logger().Errorf(err, "get consumer that rely all service failed.")
- return nil, err
- }
- dependency := &pb.MicroServiceDependency{}
- if len(rsp.Kvs) != 0 {
- util.Logger().Infof("consumer that rely all service exist.ServiceName: %s.", dr.provider.ServiceName)
- err = json.Unmarshal(rsp.Kvs[0].Value, dependency)
- if err != nil {
- return nil, err
- }
- return dependency.Dependency, nil
- }
- return dependency.Dependency, nil
-}
-
-func (dr *DependencyRelation) getConsumerOfSameServiceNameAndAppId(provider *pb.MicroServiceKey) ([]*pb.MicroServiceKey, error) {
- providerVersion := provider.Version
- provider.Version = ""
- prefix := apt.GenerateProviderDependencyRuleKey(dr.domainProject, provider)
- provider.Version = providerVersion
-
- opts := append(FromContext(dr.ctx),
- registry.WithStrKey(prefix),
- registry.WithPrefix())
- rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
- if err != nil {
- util.Logger().Errorf(err, "get all dependency rule failed: provider rule key %v.", provider)
- return nil, err
- }
-
- allConsumers := make([]*pb.MicroServiceKey, 0, len(rsp.Kvs))
- var latestServiceId []string
-
- for _, kv := range rsp.Kvs {
- dependency := &pb.MicroServiceDependency{
- Dependency: []*pb.MicroServiceKey{},
- }
- providerVersionRuleArr := strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
- providerVersionRule := providerVersionRuleArr[len(providerVersionRuleArr)-1]
- if providerVersionRule == "latest" {
- if latestServiceId == nil {
- latestServiceId, err = FindServiceIds(dr.ctx, providerVersionRule, provider)
- if err != nil {
- util.Logger().Errorf(err, "Get latest service failed.")
- return nil, err
- }
- }
- if len(latestServiceId) == 0 {
- util.Logger().Infof("%s 's providerId is empty,no this service.", provider.ServiceName)
- continue
- }
- if dr.provider.ServiceId != latestServiceId[0] {
- continue
- }
-
- } else {
- if !VersionMatchRule(providerVersion, providerVersionRule) {
- continue
- }
- }
-
- util.Logger().Debugf("providerETCD is %s", providerVersionRuleArr)
- err = json.Unmarshal(kv.Value, dependency)
- if err != nil {
- util.Logger().Errorf(err, "Unmarshal consumers failed.")
- return nil, err
- }
- allConsumers = append(allConsumers, dependency.Dependency...)
- }
- return allConsumers, nil
-}
-
-func DeleteDependencyForDeleteService(domainProject string, serviceId string, service *pb.MicroServiceKey) (registry.PluginOp, error) {
- key := apt.GenerateConsumerDependencyQueueKey(domainProject, serviceId, "0")
- conDep := new(pb.ConsumerDependency)
- conDep.Consumer = service
- conDep.Providers = []*pb.MicroServiceKey{}
- conDep.Override = true
- data, err := json.Marshal(conDep)
- if err != nil {
- return registry.PluginOp{}, err
- }
- return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
-}
-
-type DependencyRelationFilterOpt struct {
- SameDomainProject bool
- NonSelf bool
-}
-
-type DependencyRelationFilterOption func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt
-
-func WithSameDomainProject() DependencyRelationFilterOption {
- return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
- opt.SameDomainProject = true
- return opt
- }
-}
-func WithoutSelfDependency() DependencyRelationFilterOption {
- return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
- opt.NonSelf = true
- return opt
- }
-}
-
-func toDependencyRelationFilterOpt(opts ...DependencyRelationFilterOption) (op DependencyRelationFilterOpt) {
- for _, opt := range opts {
- op = opt(op)
- }
- return
-}
diff --git a/server/service/util/dependency_query.go b/server/service/util/dependency_query.go
new file mode 100644
index 00000000..343e9fa7
--- /dev/null
+++ b/server/service/util/dependency_query.go
@@ -0,0 +1,351 @@
+/*
+ * 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 util
+
+import (
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ apt "github.com/apache/incubator-servicecomb-service-center/server/core"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ "github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+ "golang.org/x/net/context"
+ "strings"
+)
+
+type DependencyRelationFilterOpt struct {
+ SameDomainProject bool
+ NonSelf bool
+}
+
+type DependencyRelationFilterOption func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt
+
+func WithSameDomainProject() DependencyRelationFilterOption {
+ return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
+ opt.SameDomainProject = true
+ return opt
+ }
+}
+func WithoutSelfDependency() DependencyRelationFilterOption {
+ return func(opt DependencyRelationFilterOpt) DependencyRelationFilterOpt {
+ opt.NonSelf = true
+ return opt
+ }
+}
+
+func toDependencyRelationFilterOpt(opts ...DependencyRelationFilterOption) (op DependencyRelationFilterOpt) {
+ for _, opt := range opts {
+ op = opt(op)
+ }
+ return
+}
+
+type DependencyRelation struct {
+ ctx context.Context
+ domainProject string
+ consumer *pb.MicroService
+ provider *pb.MicroService
+}
+
+func (dr *DependencyRelation) GetDependencyProviders(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
+ keys, err := dr.getProviderKeys()
+ if err != nil {
+ return nil, err
+ }
+ services := make([]*pb.MicroService, 0, len(keys))
+ op := toDependencyRelationFilterOpt(opts...)
+ for _, key := range keys {
+ if op.SameDomainProject && key.Tenant != dr.domainProject {
+ continue
+ }
+
+ providerIds, err := dr.parseDependencyRule(key)
+ if err != nil {
+ return nil, err
+ }
+
+ if key.ServiceName == "*" {
+ services = services[:0]
+ }
+
+ for _, providerId := range providerIds {
+ provider, err := GetService(dr.ctx, key.Tenant, providerId)
+ if err != nil {
+ util.Logger().Warnf(nil, "Provider does not exist, %s/%s/%s",
+ key.AppId, key.ServiceName, key.Version)
+ continue
+ }
+ if provider == nil {
+ continue
+ }
+ if op.NonSelf && providerId == dr.consumer.ServiceId {
+ continue
+ }
+ services = append(services, provider)
+ }
+
+ if key.ServiceName == "*" {
+ break
+ }
+ }
+ return services, nil
+}
+
+func (dr *DependencyRelation) GetDependencyProviderIds() ([]string, error) {
+ keys, err := dr.getProviderKeys()
+ if err != nil {
+ return nil, err
+ }
+ return dr.getDependencyProviderIds(keys)
+}
+
+func (dr *DependencyRelation) getProviderKeys() ([]*pb.MicroServiceKey, error) {
+ if dr.consumer == nil {
+ util.LOGGER.Infof("dr.consumer is nil ------->")
+ return nil, fmt.Errorf("Invalid consumer")
+ }
+ consumerMicroServiceKey := pb.MicroServiceToKey(dr.domainProject, dr.consumer)
+
+ conKey := apt.GenerateConsumerDependencyRuleKey(dr.domainProject, consumerMicroServiceKey)
+ consumerDependency, err := TransferToMicroServiceDependency(dr.ctx, conKey)
+ if err != nil {
+ return nil, err
+ }
+ return consumerDependency.Dependency, nil
+}
+
+func (dr *DependencyRelation) getDependencyProviderIds(providerRules []*pb.MicroServiceKey) ([]string, error) {
+ provideServiceIds := make([]string, 0, len(providerRules))
+ for _, provider := range providerRules {
+ serviceIds, err := dr.parseDependencyRule(provider)
+ switch {
+ case provider.ServiceName == "*":
+ if err != nil {
+ util.Logger().Errorf(err, "Add dependency failed, rely all service: get all services failed.")
+ return provideServiceIds, err
+ }
+ return serviceIds, nil
+ default:
+ if err != nil {
+ util.Logger().Errorf(err, "Get providerIds failed, service: %s/%s/%s",
+ provider.AppId, provider.ServiceName, provider.Version)
+ return provideServiceIds, err
+ }
+ if len(serviceIds) == 0 {
+ util.Logger().Warnf(nil, "Get providerIds is empty, service: %s/%s/%s does not exist",
+ provider.AppId, provider.ServiceName, provider.Version)
+ continue
+ }
+ provideServiceIds = append(provideServiceIds, serviceIds...)
+ }
+ }
+ return provideServiceIds, nil
+}
+
+func (dr *DependencyRelation) parseDependencyRule(dependencyRule *pb.MicroServiceKey) (serviceIds []string, err error) {
+ opts := FromContext(dr.ctx)
+ switch {
+ case dependencyRule.ServiceName == "*":
+ util.Logger().Infof("Rely all service, * type, consumerId %s", dr.consumer.ServiceId)
+ splited := strings.Split(apt.GenerateServiceIndexKey(dependencyRule), "/")
+ allServiceKey := util.StringJoin(splited[:len(splited)-3], "/") + "/"
+ sopts := append(opts,
+ registry.WithStrKey(allServiceKey),
+ registry.WithPrefix())
+ resp, err := backend.Store().Service().Search(dr.ctx, sopts...)
+ if err != nil {
+ return nil, err
+ }
+
+ for _, kv := range resp.Kvs {
+ serviceIds = append(serviceIds, kv.Value.(string))
+ }
+ default:
+ serviceIds, err = FindServiceIds(dr.ctx, dependencyRule.Version, dependencyRule)
+ }
+ return
+}
+
+func (dr *DependencyRelation) GetDependencyConsumers(opts ...DependencyRelationFilterOption) ([]*pb.MicroService, error) {
+ consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
+ if err != nil {
+ util.Logger().Errorf(err, "Get consumers of provider rule failed, %s", dr.provider.ServiceId)
+ return nil, err
+ }
+ consumers := make([]*pb.MicroService, 0)
+ op := toDependencyRelationFilterOpt(opts...)
+ for _, consumer := range consumerDependAllList {
+ if op.SameDomainProject && consumer.Tenant != dr.domainProject {
+ continue
+ }
+
+ service, err := dr.getServiceByMicroServiceKey(consumer)
+ if err != nil {
+ return nil, err
+ }
+ if service == nil {
+ util.Logger().Warnf(nil, "Consumer does not exist, %v", consumer)
+ continue
+ }
+
+ if op.NonSelf && service.ServiceId == dr.provider.ServiceId {
+ continue
+ }
+
+ consumers = append(consumers, service)
+ }
+ return consumers, nil
+}
+
+func (dr *DependencyRelation) getServiceByMicroServiceKey(service *pb.MicroServiceKey) (*pb.MicroService, error) {
+ serviceId, err := GetServiceId(dr.ctx, service)
+ if err != nil {
+ return nil, err
+ }
+ if len(serviceId) == 0 {
+ util.Logger().Warnf(nil, "Service not exist,%v", service)
+ return nil, nil
+ }
+ return GetService(dr.ctx, service.Tenant, serviceId)
+}
+
+func (dr *DependencyRelation) GetDependencyConsumerIds() ([]string, error) {
+ consumerDependAllList, err := dr.getDependencyConsumersOfProvider()
+ if err != nil {
+ return nil, err
+ }
+ consumerIds := make([]string, 0, len(consumerDependAllList))
+ for _, consumer := range consumerDependAllList {
+ consumerId, err := GetServiceId(dr.ctx, consumer)
+ if err != nil {
+ util.Logger().Errorf(err, "Get consumer failed, %v", consumer)
+ return nil, err
+ }
+ if len(consumerId) == 0 {
+ util.Logger().Warnf(nil, "Get consumer not exist, %v", consumer)
+ continue
+ }
+ consumerIds = append(consumerIds, consumerId)
+ }
+ return consumerIds, nil
+
+}
+
+func (dr *DependencyRelation) getDependencyConsumersOfProvider() ([]*pb.MicroServiceKey, error) {
+ if dr.provider == nil {
+ util.LOGGER.Infof("dr.provider is nil ------->")
+ return nil, fmt.Errorf("Invalid provider")
+ }
+ providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
+ consumerDependAllList, err := dr.getConsumerOfDependAllServices()
+ if err != nil {
+ util.Logger().Errorf(err, "Get consumer that depend on all services failed, %s", dr.provider.ServiceId)
+ return nil, err
+ }
+
+ consumerDependList, err := dr.getConsumerOfSameServiceNameAndAppId(providerService)
+ if err != nil {
+ util.Logger().Errorf(err, "Get consumer that depend on same serviceName and appid rule failed, %s",
+ dr.provider.ServiceId)
+ return nil, err
+ }
+ consumerDependAllList = append(consumerDependAllList, consumerDependList...)
+ return consumerDependAllList, nil
+}
+
+func (dr *DependencyRelation) getConsumerOfDependAllServices() ([]*pb.MicroServiceKey, error) {
+ providerService := pb.MicroServiceToKey(dr.domainProject, dr.provider)
+ providerService.ServiceName = "*"
+ relyAllKey := apt.GenerateProviderDependencyRuleKey(dr.domainProject, providerService)
+ opts := append(FromContext(dr.ctx), registry.WithStrKey(relyAllKey))
+ rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
+ if err != nil {
+ util.Logger().Errorf(err, "get consumer that rely all service failed.")
+ return nil, err
+ }
+ if len(rsp.Kvs) != 0 {
+ util.Logger().Infof("consumer that rely all service exist.ServiceName: %s.", dr.provider.ServiceName)
+ return rsp.Kvs[0].Value.(*pb.MicroServiceDependency).Dependency, nil
+ }
+ return nil, nil
+}
+
+func (dr *DependencyRelation) getConsumerOfSameServiceNameAndAppId(provider *pb.MicroServiceKey) ([]*pb.MicroServiceKey, error) {
+ providerVersion := provider.Version
+ provider.Version = ""
+ prefix := apt.GenerateProviderDependencyRuleKey(dr.domainProject, provider)
+ provider.Version = providerVersion
+
+ opts := append(FromContext(dr.ctx),
+ registry.WithStrKey(prefix),
+ registry.WithPrefix())
+ rsp, err := backend.Store().DependencyRule().Search(dr.ctx, opts...)
+ if err != nil {
+ util.Logger().Errorf(err, "get all dependency rule failed: provider rule key %v.", provider)
+ return nil, err
+ }
+
+ allConsumers := make([]*pb.MicroServiceKey, 0, len(rsp.Kvs))
+ var latestServiceId []string
+
+ for _, kv := range rsp.Kvs {
+ providerVersionRuleArr := strings.Split(util.BytesToStringWithNoCopy(kv.Key), "/")
+ providerVersionRule := providerVersionRuleArr[len(providerVersionRuleArr)-1]
+ if providerVersionRule == "latest" {
+ if latestServiceId == nil {
+ latestServiceId, err = FindServiceIds(dr.ctx, providerVersionRule, provider)
+ if err != nil {
+ util.Logger().Errorf(err, "Get latest service failed.")
+ return nil, err
+ }
+ }
+ if len(latestServiceId) == 0 {
+ util.Logger().Infof("%s 's providerId is empty,no this service.", provider.ServiceName)
+ continue
+ }
+ if dr.provider.ServiceId != latestServiceId[0] {
+ continue
+ }
+
+ } else {
+ if !VersionMatchRule(providerVersion, providerVersionRule) {
+ continue
+ }
+ }
+
+ util.Logger().Debugf("providerETCD is %s", providerVersionRuleArr)
+ allConsumers = append(allConsumers, kv.Value.(*pb.MicroServiceDependency).Dependency...)
+ }
+ return allConsumers, nil
+}
+
+func NewProviderDependencyRelation(ctx context.Context, domainProject string, provider *pb.MicroService) *DependencyRelation {
+ return NewDependencyRelation(ctx, domainProject, nil, provider)
+}
+
+func NewConsumerDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService) *DependencyRelation {
+ return NewDependencyRelation(ctx, domainProject, consumer, nil)
+}
+
+func NewDependencyRelation(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroService) *DependencyRelation {
+ return &DependencyRelation{
+ ctx: ctx,
+ domainProject: domainProject,
+ consumer: consumer,
+ provider: provider,
+ }
+}
diff --git a/server/service/util/dependency_test.go b/server/service/util/dependency_test.go
index ab63fd7d..af7b3a4b 100644
--- a/server/service/util/dependency_test.go
+++ b/server/service/util/dependency_test.go
@@ -17,7 +17,6 @@
package util
import (
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
"golang.org/x/net/context"
"testing"
@@ -28,51 +27,10 @@ func TestDeleteDependencyForService(t *testing.T) {
if err != nil {
t.Fatalf(`DeleteDependencyForDeleteService failed`)
}
-
- _, err = updateProviderDependencyRuleUtil(
- &proto.MicroServiceDependency{
- Dependency: []*proto.MicroServiceKey{
- {AppId: "a"},
- },
- },
- &proto.MicroServiceKey{
- AppId: "a",
- }, "")
- if err != nil {
- t.Fatalf(`deleteDependencyRuleUtil with the same deps failed`)
- }
-
- _, err = updateProviderDependencyRuleUtil(
- &proto.MicroServiceDependency{
- Dependency: []*proto.MicroServiceKey{
- {AppId: "b"},
- },
- },
- &proto.MicroServiceKey{
- AppId: "a",
- }, "")
- if err != nil {
- t.Fatalf(`deleteDependencyRuleUtil failed`)
- }
-
- _, err = deleteConsumerDepOfProviderRule(context.Background(), "", &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
- if err == nil {
- t.Fatalf(`deleteConsumerDepOfProviderRule failed`)
- }
-
- _, err = deleteDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`deleteDepRuleUtil failed`)
- }
}
func TestTransferToMicroServiceDependency(t *testing.T) {
- _, err := TransferToMicroServiceDependency(util.SetContext(context.Background(), "cacheOnly", "1"), "")
- if err != nil {
- t.Fatalf(`TransferToMicroServiceDependency WithCacheOnly failed`)
- }
-
- _, err = TransferToMicroServiceDependency(context.Background(), "")
+ _, err := TransferToMicroServiceDependency(context.Background(), "")
if err == nil {
t.Fatalf(`TransferToMicroServiceDependency failed`)
}
@@ -114,11 +72,6 @@ func TestCreateDependencyRule(t *testing.T) {
t.Fatalf(`AddServiceVersionRule failed`)
}
- _, err = addDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`addDepRuleUtil failed`)
- }
-
b, err := containServiceDependency([]*proto.MicroServiceKey{
{AppId: "a"},
}, &proto.MicroServiceKey{
@@ -154,33 +107,6 @@ func TestCreateDependencyRule(t *testing.T) {
if !ok {
t.Fatalf(`diffServiceVersion failed`)
}
-
- ok = isDependencyAll(&proto.MicroServiceDependency{})
- if ok {
- t.Fatalf(`isDependencyAll not * failed`)
- }
-
- ok = isDependencyAll(&proto.MicroServiceDependency{
- Dependency: []*proto.MicroServiceKey{
- {
- ServiceName: "*",
- },
- },
- })
- if !ok {
- t.Fatalf(`isDependencyAll * failed`)
- }
-
- ok = isExist([]*proto.MicroServiceKey{
- {
- ServiceName: "*",
- },
- }, &proto.MicroServiceKey{
- ServiceName: "*",
- })
- if !ok {
- t.Fatalf(`isExist failed`)
- }
}
func TestBadParamsResponse(t *testing.T) {
@@ -248,23 +174,13 @@ func TestParamsChecker(t *testing.T) {
}
func TestServiceDependencyRuleExist(t *testing.T) {
- _, err := DependencyRuleExist(util.SetContext(context.Background(), "cacheOnly", "1"), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`ServiceDependencyRuleExist WithCacheOnly failed`)
- }
-
- _, err = DependencyRuleExist(context.Background(), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
+ _, err := DependencyRuleExist(context.Background(), &proto.MicroServiceKey{}, &proto.MicroServiceKey{})
if err == nil {
t.Fatalf(`ServiceDependencyRuleExist failed`)
}
}
func TestUpdateServiceForAddDependency(t *testing.T) {
- _, _, err := updateDepRuleUtil("", &proto.MicroServiceDependency{}, &proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`updateDepRuleUtil failed`)
- }
-
old := isNeedUpdate([]*proto.MicroServiceKey{
{
AppId: "a",
@@ -281,33 +197,6 @@ func TestUpdateServiceForAddDependency(t *testing.T) {
}
}
-func TestFilter(t *testing.T) {
- _, _, err := getConsumerIdsWithFilter(context.Background(), "", &proto.MicroService{}, noFilter)
- if err != nil {
- t.Fatalf(`getConsumerIdsWithFilter failed`)
- }
-
- _, _, err = filterConsumerIds(context.Background(), []string{}, noFilter)
- if err != nil {
- t.Fatalf(`filterConsumerIds invalid failed`)
- }
-
- _, _, err = filterConsumerIds(context.Background(), []string{"a"}, noFilter)
- if err != nil {
- t.Fatalf(`filterConsumerIds invalid failed`)
- }
-
- rf := RuleFilter{
- DomainProject: "",
- Provider: &proto.MicroService{},
- ProviderRules: []*proto.ServiceRule{},
- }
- _, _, err = filterConsumerIds(context.Background(), []string{"a"}, rf.Filter)
- if err != nil {
- t.Fatalf(`filterConsumerIds invalid failed`)
- }
-}
-
func TestDependency(t *testing.T) {
d := &Dependency{
removedDependencyRuleList: []*proto.MicroServiceKey{
@@ -325,47 +214,6 @@ func TestDependency(t *testing.T) {
}
dr := &DependencyRelation{
- provider: &proto.MicroService{},
- consumer: &proto.MicroService{},
- ctx: util.SetContext(context.Background(), "cacheOnly", "1"),
- }
- _, err = dr.GetDependencyProviders()
- if err != nil {
- t.Fatalf(`DependencyRelation_GetDependencyProviders failed`)
- }
-
- _, err = dr.getDependencyProviderIds([]*proto.MicroServiceKey{
- {ServiceName: "*"},
- {ServiceName: "a", Version: "1.0.0"},
- {ServiceName: "b", Version: "latest"},
- })
- if err != nil {
- t.Fatalf(`DependencyRelation_getDependencyProviderIds * WithCacheOnly failed`)
- }
- _, err = dr.getDependencyProviderIds([]*proto.MicroServiceKey{
- {ServiceName: "a", Version: "1.0.0"},
- {ServiceName: "b", Version: "latest"},
- })
- if err != nil {
- t.Fatalf(`DependencyRelation_getDependencyProviderIds WithCacheOnly failed`)
- }
-
- _, err = dr.GetDependencyConsumers()
- if err != nil {
- t.Fatalf(`DependencyRelation_GetDependencyConsumers WithCacheOnly failed`)
- }
-
- _, err = dr.getServiceByMicroServiceKey(&proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`DependencyRelation_getServiceByMicroServiceKey WithCacheOnly failed`)
- }
-
- _, err = dr.getConsumerOfSameServiceNameAndAppId(&proto.MicroServiceKey{})
- if err != nil {
- t.Fatalf(`DependencyRelation_getConsumerOfSameServiceNameAndAppId WithCacheOnly failed`)
- }
-
- dr = &DependencyRelation{
provider: &proto.MicroService{},
consumer: &proto.MicroService{},
ctx: context.Background(),
@@ -407,6 +255,10 @@ func TestDependency(t *testing.T) {
if err == nil {
t.Fatalf(`DependencyRelation_getDependencyConsumersOfProvider failed`)
}
+ _, err = dr.GetDependencyProviders()
+ if err == nil {
+ t.Fatalf(`DependencyRelation_GetDependencyProviders failed`)
+ }
}
func TestDependencyRelationFilterOpt(t *testing.T) {
@@ -418,3 +270,10 @@ func TestDependencyRelationFilterOpt(t *testing.T) {
t.Fatalf(`toDependencyRelationFilterOpt failed`)
}
}
+
+func TestGetConsumerIdsWithFilter(t *testing.T) {
+ _, _, err := getConsumerIdsWithFilter(context.Background(), "", &proto.MicroService{}, nil)
+ if err == nil {
+ t.Fatalf(`TestGetConsumerIdsWithFilter failed`)
+ }
+}
diff --git a/server/service/util/dependency_util.go b/server/service/util/dependency_util.go
new file mode 100644
index 00000000..f7f4aae3
--- /dev/null
+++ b/server/service/util/dependency_util.go
@@ -0,0 +1,427 @@
+/*
+ * 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 util
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/apache/incubator-servicecomb-service-center/pkg/util"
+ apt "github.com/apache/incubator-servicecomb-service-center/server/core"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/backend"
+ pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
+ scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
+ "github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
+ "golang.org/x/net/context"
+ "strings"
+)
+
+func GetConsumerIds(ctx context.Context, domainProject string, provider *pb.MicroService) ([]string, error) {
+ // 查询所有consumer
+ dr := NewProviderDependencyRelation(ctx, domainProject, provider)
+ consumerIds, err := dr.GetDependencyConsumerIds()
+ if err != nil {
+ util.Logger().Errorf(err, "Get dependency consumerIds failed.%s", provider.ServiceId)
+ return nil, err
+ }
+ return consumerIds, nil
+}
+
+func GetProviderIds(ctx context.Context, domainProject string, consumer *pb.MicroService) ([]string, error) {
+ // 查询所有provider
+ dr := NewConsumerDependencyRelation(ctx, domainProject, consumer)
+ providerIds, err := dr.GetDependencyProviderIds()
+ if err != nil {
+ util.Logger().Errorf(err, "Get dependency providerIds failed.%s", consumer.ServiceId)
+ return nil, err
+ }
+ return providerIds, nil
+}
+
+// GetAllConsumerIds is the function get from dependency rule and filter with service rules
+func GetAllConsumerIds(ctx context.Context, domainProject string, provider *pb.MicroService) (allow []string, deny []string, _ error) {
+ if provider == nil || len(provider.ServiceId) == 0 {
+ return nil, nil, fmt.Errorf("invalid provider")
+ }
+
+ //todo 删除服务,最后实例推送有误差
+ providerRules, err := GetRulesUtil(ctx, domainProject, provider.ServiceId)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ rf := &RuleFilter{
+ DomainProject: domainProject,
+ ProviderRules: providerRules,
+ }
+
+ allow, deny, err = getConsumerIdsWithFilter(ctx, domainProject, provider, rf)
+ if err != nil {
+ return nil, nil, err
+ }
+ return allow, deny, nil
+}
+
+func getConsumerIdsWithFilter(ctx context.Context, domainProject string, provider *pb.MicroService, rf *RuleFilter) (allow []string, deny []string, err error) {
+ consumerIds, err := GetConsumerIds(ctx, domainProject, provider)
+ if err != nil {
+ return nil, nil, err
+ }
+ return rf.FilterAll(ctx, consumerIds)
+}
+
+func GetAllProviderIds(ctx context.Context, domainProject string, service *pb.MicroService) (allow []string, deny []string, _ error) {
+ providerIdsInCache, err := GetProviderIds(ctx, domainProject, service)
+ if err != nil {
+ return nil, nil, err
+ }
+ l := len(providerIdsInCache)
+ rf := RuleFilter{
+ DomainProject: domainProject,
+ }
+ allowIdx, denyIdx := 0, l
+ providerIds := make([]string, l)
+ copyCtx := util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1")
+ for _, providerId := range providerIdsInCache {
+ providerRules, err := GetRulesUtil(copyCtx, domainProject, providerId)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(providerRules) == 0 {
+ providerIds[allowIdx] = providerId
+ allowIdx++
+ continue
+ }
+ rf.ProviderRules = providerRules
+ ok, err := rf.Filter(ctx, service.ServiceId)
+ if err != nil {
+ return nil, nil, err
+ }
+ if ok {
+ providerIds[allowIdx] = providerId
+ allowIdx++
+ } else {
+ denyIdx--
+ providerIds[denyIdx] = providerId
+ }
+ }
+ return providerIds[:allowIdx], providerIds[denyIdx:], nil
+}
+
+func DependencyRuleExist(ctx context.Context, provider *pb.MicroServiceKey, consumer *pb.MicroServiceKey) (bool, error) {
+ targetDomainProject := provider.Tenant
+ if len(targetDomainProject) == 0 {
+ targetDomainProject = consumer.Tenant
+ }
+
+ consumerKey := apt.GenerateConsumerDependencyRuleKey(consumer.Tenant, consumer)
+ existed, err := dependencyRuleExistUtil(ctx, consumerKey, provider)
+ if err != nil || existed {
+ return existed, err
+ }
+
+ providerKey := apt.GenerateProviderDependencyRuleKey(targetDomainProject, provider)
+ return dependencyRuleExistUtil(ctx, providerKey, consumer)
+}
+
+func dependencyRuleExistUtil(ctx context.Context, key string, target *pb.MicroServiceKey) (bool, error) {
+ compareData, err := TransferToMicroServiceDependency(ctx, key)
+ if err != nil {
+ return false, err
+ }
+ if len(compareData.Dependency) != 0 {
+ isEqual, err := containServiceDependency(compareData.Dependency, target)
+ if err != nil {
+ return false, err
+ }
+ if isEqual {
+ //删除之前的依赖
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func AddServiceVersionRule(ctx context.Context, domainProject string, consumer *pb.MicroService, provider *pb.MicroServiceKey) error {
+ //创建依赖一致
+ consumerKey := pb.MicroServiceToKey(domainProject, consumer)
+ exist, err := DependencyRuleExist(ctx, provider, consumerKey)
+ if exist || err != nil {
+ return err
+ }
+
+ r := &pb.ConsumerDependency{
+ Consumer: consumerKey,
+ Providers: []*pb.MicroServiceKey{provider},
+ Override: false,
+ }
+ data, err := json.Marshal(r)
+ if err != nil {
+ return err
+ }
+
+ id := util.StringJoin([]string{provider.AppId, provider.ServiceName}, "_")
+ key := apt.GenerateConsumerDependencyQueueKey(domainProject, consumer.ServiceId, id)
+ resp, err := backend.Registry().TxnWithCmp(ctx,
+ nil,
+ []registry.CompareOp{registry.OpCmp(registry.CmpStrVal(key), registry.CMP_EQUAL, util.BytesToStringWithNoCopy(data))},
+ []registry.PluginOp{registry.OpPut(registry.WithStrKey(key), registry.WithValue(data))})
+ if err != nil {
+ return err
+ }
+ if !resp.Succeeded {
+ util.Logger().Infof("find request into dependency queue successfully, %s: %v", key, r)
+ }
+ return nil
+}
+
+func TransferToMicroServiceDependency(ctx context.Context, key string) (*pb.MicroServiceDependency, error) {
+ microServiceDependency := &pb.MicroServiceDependency{
+ Dependency: []*pb.MicroServiceKey{},
+ }
+
+ opts := append(FromContext(ctx), registry.WithStrKey(key))
+ res, err := backend.Store().DependencyRule().Search(ctx, opts...)
+ if err != nil {
+ util.Logger().Errorf(nil, "Get dependency rule failed.")
+ return nil, err
+ }
+ if len(res.Kvs) != 0 {
+ return res.Kvs[0].Value.(*pb.MicroServiceDependency), nil
+ }
+ return microServiceDependency, nil
+}
+
+func equalServiceDependency(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
+ stringA := toString(serviceA)
+ stringB := toString(serviceB)
+ if stringA == stringB {
+ return true
+ }
+ return false
+}
+
+func diffServiceVersion(serviceA *pb.MicroServiceKey, serviceB *pb.MicroServiceKey) bool {
+ stringA := toString(serviceA)
+ stringB := toString(serviceB)
+ if stringA != stringB &&
+ stringA[:strings.LastIndex(stringA, "/")+1] == stringB[:strings.LastIndex(stringB, "/")+1] {
+ return true
+ }
+ return false
+}
+
+func toString(in *pb.MicroServiceKey) string {
+ return apt.GenerateProviderDependencyRuleKey(in.Tenant, in)
+}
+
+func parseAddOrUpdateRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
+ conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
+
+ oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
+ if err != nil {
+ util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
+ dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
+ return
+ }
+
+ deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
+ newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
+ existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
+ for _, tmpProviderRule := range dep.ProvidersRule {
+ if ok, _ := containServiceDependency(oldProviderRules.Dependency, tmpProviderRule); ok {
+ continue
+ }
+
+ if tmpProviderRule.ServiceName == "*" {
+ newDependencyRuleList = append([]*pb.MicroServiceKey{}, tmpProviderRule)
+ deleteDependencyRuleList = oldProviderRules.Dependency
+ break
+ }
+
+ newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
+ old := isNeedUpdate(oldProviderRules.Dependency, tmpProviderRule)
+ if old != nil {
+ deleteDependencyRuleList = append(deleteDependencyRuleList, old)
+ }
+ }
+ for _, oldProviderRule := range oldProviderRules.Dependency {
+ if oldProviderRule.ServiceName == "*" {
+ newDependencyRuleList = nil
+ deleteDependencyRuleList = nil
+ return
+ }
+ if ok, _ := containServiceDependency(deleteDependencyRuleList, oldProviderRule); !ok {
+ existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
+ }
+ }
+
+ dep.ProvidersRule = append(newDependencyRuleList, existDependencyRuleList...)
+ return
+}
+
+func parseOverrideRules(ctx context.Context, dep *Dependency) (newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList []*pb.MicroServiceKey) {
+ conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
+
+ oldProviderRules, err := TransferToMicroServiceDependency(ctx, conKey)
+ if err != nil {
+ util.Logger().Errorf(err, "maintain dependency rule failed, consumer %s/%s/%s: get consumer depedency rule failed.",
+ dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version)
+ return
+ }
+
+ deleteDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
+ newDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(dep.ProvidersRule))
+ existDependencyRuleList = make([]*pb.MicroServiceKey, 0, len(oldProviderRules.Dependency))
+ for _, oldProviderRule := range oldProviderRules.Dependency {
+ if ok, _ := containServiceDependency(dep.ProvidersRule, oldProviderRule); !ok {
+ deleteDependencyRuleList = append(deleteDependencyRuleList, oldProviderRule)
+ } else {
+ existDependencyRuleList = append(existDependencyRuleList, oldProviderRule)
+ }
+ }
+ for _, tmpProviderRule := range dep.ProvidersRule {
+ if ok, _ := containServiceDependency(existDependencyRuleList, tmpProviderRule); !ok {
+ newDependencyRuleList = append(newDependencyRuleList, tmpProviderRule)
+ }
+ }
+ return
+}
+
+func syncDependencyRule(ctx context.Context, dep *Dependency, filter func(context.Context, *Dependency) (_, _, _ []*pb.MicroServiceKey)) error {
+ //更新consumer的providers的值,consumer的版本是确定的
+ consumerFlag := strings.Join([]string{dep.Consumer.AppId, dep.Consumer.ServiceName, dep.Consumer.Version}, "/")
+
+ newDependencyRuleList, existDependencyRuleList, deleteDependencyRuleList := filter(ctx, dep)
+ if len(newDependencyRuleList) == 0 && len(existDependencyRuleList) == 0 && len(deleteDependencyRuleList) == 0 {
+ return nil
+ }
+
+ dep.err = make(chan error, 5)
+ dep.chanNum = 0
+ if len(deleteDependencyRuleList) != 0 {
+ util.Logger().Infof("Delete dependency rule remove for consumer %s, %v, ", consumerFlag, deleteDependencyRuleList)
+ dep.removedDependencyRuleList = deleteDependencyRuleList
+ dep.RemoveConsumerOfProviderRule()
+ }
+
+ if len(newDependencyRuleList) != 0 {
+ util.Logger().Infof("New dependency rule add for consumer %s, %v, ", consumerFlag, newDependencyRuleList)
+ dep.NewDependencyRuleList = newDependencyRuleList
+ dep.AddConsumerOfProviderRule()
+ }
+
+ conKey := apt.GenerateConsumerDependencyRuleKey(dep.DomainProject, dep.Consumer)
+ err := dep.UpdateProvidersRuleOfConsumer(conKey)
+ if err != nil {
+ return err
+ }
+
+ if dep.chanNum != 0 {
+ for tmpErr := range dep.err {
+ dep.chanNum--
+ if tmpErr != nil {
+ return tmpErr
+ }
+ if 0 == dep.chanNum {
+ close(dep.err)
+ }
+ }
+ }
+ return nil
+}
+
+func AddDependencyRule(ctx context.Context, dep *Dependency) error {
+ return syncDependencyRule(ctx, dep, parseAddOrUpdateRules)
+}
+
+func CreateDependencyRule(ctx context.Context, dep *Dependency) error {
+ return syncDependencyRule(ctx, dep, parseOverrideRules)
+}
+
+func isNeedUpdate(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) *pb.MicroServiceKey {
+ for _, tmp := range services {
+ if diffServiceVersion(tmp, service) {
+ return tmp
+ }
+ }
+ return nil
+}
+
+func containServiceDependency(services []*pb.MicroServiceKey, service *pb.MicroServiceKey) (bool, error) {
+ if services == nil || service == nil {
+ return false, errors.New("Invalid params input.")
+ }
+ for _, value := range services {
+ rst := equalServiceDependency(service, value)
+ if rst {
+ return true, nil
+ }
+ }
+ return false, nil
+}
+
+func BadParamsResponse(detailErr string) *pb.CreateDependenciesResponse {
+ util.Logger().Errorf(nil, "Request params is invalid. %s", detailErr)
+ if len(detailErr) == 0 {
+ detailErr = "Request params is invalid."
+ }
+ return &pb.CreateDependenciesResponse{
+ Response: pb.CreateResponse(scerr.ErrInvalidParams, detailErr),
+ }
+}
+
+func ParamsChecker(consumerInfo *pb.MicroServiceKey, providersInfo []*pb.MicroServiceKey) *pb.CreateDependenciesResponse {
+ flag := make(map[string]bool, len(providersInfo))
+ for _, providerInfo := range providersInfo {
+ //存在带*的情况,后面的数据就不校验了
+ if providerInfo.ServiceName == "*" {
+ util.Logger().Debugf("%s 's provider contains *.", consumerInfo.ServiceName)
+ break
+ }
+ if len(providerInfo.AppId) == 0 {
+ providerInfo.AppId = consumerInfo.AppId
+ }
+
+ version := providerInfo.Version
+ if len(version) == 0 {
+ return BadParamsResponse("Required provider version")
+ }
+
+ providerInfo.Version = ""
+ if _, ok := flag[toString(providerInfo)]; ok {
+ return BadParamsResponse("Invalid request body for provider info.Duplicate provider or (serviceName and appId is same).")
+ } else {
+ flag[toString(providerInfo)] = true
+ }
+ providerInfo.Version = version
+ }
+ return nil
+}
+
+func DeleteDependencyForDeleteService(domainProject string, serviceId string, service *pb.MicroServiceKey) (registry.PluginOp, error) {
+ key := apt.GenerateConsumerDependencyQueueKey(domainProject, serviceId, "0")
+ conDep := new(pb.ConsumerDependency)
+ conDep.Consumer = service
+ conDep.Providers = []*pb.MicroServiceKey{}
+ conDep.Override = true
+ data, err := json.Marshal(conDep)
+ if err != nil {
+ return registry.PluginOp{}, err
+ }
+ return registry.OpPut(registry.WithStrKey(key), registry.WithValue(data)), nil
+}
diff --git a/server/service/util/domain_util.go b/server/service/util/domain_util.go
index 1ae8f6f6..ede7ca7f 100644
--- a/server/service/util/domain_util.go
+++ b/server/service/util/domain_util.go
@@ -21,12 +21,11 @@ import (
apt "github.com/apache/incubator-servicecomb-service-center/server/core"
"github.com/apache/incubator-servicecomb-service-center/server/core/backend"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"strings"
)
-func GetAllDomainRawData(ctx context.Context) ([]*mvccpb.KeyValue, error) {
+func GetAllDomainRawData(ctx context.Context) ([]*backend.KeyValue, error) {
opts := append(FromContext(ctx),
registry.WithStrKey(apt.GenerateDomainKey("")),
registry.WithPrefix())
diff --git a/server/service/util/domain_util_test.go b/server/service/util/domain_util_test.go
index 946d17fb..a86dc8f3 100644
--- a/server/service/util/domain_util_test.go
+++ b/server/service/util/domain_util_test.go
@@ -17,25 +17,28 @@
package util
import (
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
"golang.org/x/net/context"
"testing"
)
-func TestGetDomain(t *testing.T) {
- _, err := GetAllDomainRawData(util.SetContext(context.Background(), CTX_CACHEONLY, "1"))
- if err != nil {
- t.Fatalf("GetAllDomainRawData WithCacheOnly failed")
+func TestGetOneDomainProjectServiceCount(t *testing.T) {
+ _, err := GetOneDomainProjectServiceCount(context.Background(), "")
+ if err == nil {
+ t.Fatalf("GetOneDomainProjectServiceCount failed")
}
+}
- _, err = GetAllDomainRawData(context.Background())
+func TestGetOneDomainProjectInstanceCount(t *testing.T) {
+ _, err := GetOneDomainProjectInstanceCount(context.Background(), "")
if err == nil {
- t.Fatalf("GetAllDomainRawData failed")
+ t.Fatalf("GetOneDomainProjectInstanceCount failed")
}
+}
- _, err = GetAllDomain(util.SetContext(context.Background(), CTX_CACHEONLY, "1"))
- if err != nil {
- t.Fatalf("GetAllDomain WithCacheOnly failed")
+func TestGetDomain(t *testing.T) {
+ _, err := GetAllDomainRawData(context.Background())
+ if err == nil {
+ t.Fatalf("GetAllDomainRawData failed")
}
_, err = GetAllDomain(context.Background())
@@ -45,12 +48,7 @@ func TestGetDomain(t *testing.T) {
}
func TestDomainExist(t *testing.T) {
- _, err := DomainExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.Fatalf("DomainExist WithCacheOnly failed")
- }
-
- _, err = DomainExist(context.Background(), "")
+ _, err := DomainExist(context.Background(), "")
if err == nil {
t.Fatalf("DomainExist failed")
}
@@ -64,12 +62,7 @@ func TestNewDomain(t *testing.T) {
}
func TestProjectExist(t *testing.T) {
- _, err := ProjectExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
- if err != nil {
- t.Fatalf("DomainExist WithCacheOnly failed")
- }
-
- _, err = ProjectExist(context.Background(), "", "")
+ _, err := ProjectExist(context.Background(), "", "")
if err == nil {
t.Fatalf("DomainExist failed")
}
diff --git a/server/service/util/find_cache.go b/server/service/util/find_cache.go
deleted file mode 100644
index 7a4c1701..00000000
--- a/server/service/util/find_cache.go
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 util
-
-import (
- "errors"
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
- pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- "github.com/karlseguin/ccache"
-)
-
-var FindInstancesCache = &VersionRuleCache{
- c: ccache.Layered(ccache.Configure()),
-}
-
-type VersionRuleCacheItem struct {
- Instances []*pb.MicroServiceInstance
- Rev string
-}
-
-type VersionRuleCache struct {
- c *ccache.LayeredCache
-}
-
-func (c *VersionRuleCache) primaryKey(domainProject string, provider *pb.MicroServiceKey) string {
- return util.StringJoin([]string{
- domainProject,
- provider.Environment,
- provider.AppId,
- provider.ServiceName}, "/")
-}
-
-func (c *VersionRuleCache) Get(domainProject, consumer string, provider *pb.MicroServiceKey) *VersionRuleCacheItem {
- item, _ := c.c.Fetch(c.primaryKey(domainProject, provider), provider.Version, cacheTTL, func() (interface{}, error) {
- return nil, errors.New("not exist")
- })
- if item == nil || item.Expired() {
- return nil
- }
-
- if v, ok := item.Value().(*util.ConcurrentMap).Get(consumer); ok {
- return v.(*VersionRuleCacheItem)
- }
- return nil
-}
-
-func (c *VersionRuleCache) Set(domainProject, consumer string, provider *pb.MicroServiceKey, item *VersionRuleCacheItem) {
- c2, _ := c.c.Fetch(c.primaryKey(domainProject, provider), provider.Version, cacheTTL, func() (interface{}, error) {
- // new one if not exist
- return util.NewConcurrentMap(1), nil
- })
- c2.Value().(*util.ConcurrentMap).Put(consumer, item)
-}
-
-func (c *VersionRuleCache) Delete(domainProject, consumer string, provider *pb.MicroServiceKey) {
- c.c.DeleteAll(c.primaryKey(domainProject, provider))
-}
diff --git a/server/service/util/find_cache_test.go b/server/service/util/find_cache_test.go
deleted file mode 100644
index ff161bb0..00000000
--- a/server/service/util/find_cache_test.go
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 util
-
-import (
- pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
- "testing"
-)
-
-func TestVersionRuleCache_Get(t *testing.T) {
- p := &pb.MicroServiceKey{}
- c := FindInstancesCache.Get("d", "c", p)
- if c != nil {
- t.Fatalf("TestVersionRuleCache_Get failed, %v", c)
- }
-
- r := &VersionRuleCacheItem{Rev: "1"}
- FindInstancesCache.Set("d", "c", p, r)
- c = FindInstancesCache.Get("d", "c", p)
- if c == nil {
- t.Fatalf("TestVersionRuleCache_Get failed, %v", c)
- }
- if c.Rev != "1" {
- t.Fatalf("TestVersionRuleCache_Get failed, rev %d != 1", c.Rev)
- }
- c = FindInstancesCache.Get("d", "c2", p)
- if c != nil {
- t.Fatalf("TestVersionRuleCache_Get failed, %v", c)
- }
-
- p2 := &pb.MicroServiceKey{ServiceName: "p2"}
- FindInstancesCache.Set("d", "c2", p, r)
- FindInstancesCache.Set("d", "c2", p2, r)
- FindInstancesCache.Delete("d", "c", p)
- c = FindInstancesCache.Get("d", "c2", p)
- if c != nil {
- t.Fatalf("TestVersionRuleCache_Get failed, %v", c)
- }
- c = FindInstancesCache.Get("d", "c2", p2)
- if c == nil {
- t.Fatalf("TestVersionRuleCache_Get failed, %v", c)
- }
- if c.Rev != "1" {
- t.Fatalf("TestVersionRuleCache_Get failed, rev %d != 1", c.Rev)
- }
-}
diff --git a/server/service/util/instance_util.go b/server/service/util/instance_util.go
index 5a2b7e9c..65c507e5 100644
--- a/server/service/util/instance_util.go
+++ b/server/service/util/instance_util.go
@@ -25,7 +25,6 @@ import (
pb "github.com/apache/incubator-servicecomb-service-center/server/core/proto"
scerr "github.com/apache/incubator-servicecomb-service-center/server/error"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
"strconv"
"strings"
@@ -42,7 +41,7 @@ func GetLeaseId(ctx context.Context, domainProject string, serviceId string, ins
if len(resp.Kvs) <= 0 {
return -1, nil
}
- leaseID, _ := strconv.ParseInt(util.BytesToStringWithNoCopy(resp.Kvs[0].Value), 10, 64)
+ leaseID, _ := strconv.ParseInt(resp.Kvs[0].Value.(string), 10, 64)
return leaseID, nil
}
@@ -58,12 +57,7 @@ func GetInstance(ctx context.Context, domainProject string, serviceId string, in
return nil, nil
}
- var instance *pb.MicroServiceInstance
- err = json.Unmarshal(resp.Kvs[0].Value, &instance)
- if err != nil {
- return nil, err
- }
- return instance, nil
+ return resp.Kvs[0].Value.(*pb.MicroServiceInstance), nil
}
func ParseRevision(rev string) (int64, int64) {
@@ -80,81 +74,6 @@ func FormatRevision(rev, count int64) string {
return fmt.Sprintf("%d.%d", rev, count)
}
-func GetAllInstancesOfServices(ctx context.Context, domainProject string, ids []string) (
- instances []*pb.MicroServiceInstance, rev string, err error) {
- cloneCtx := util.CloneContext(ctx)
- noCache, cacheOnly := ctx.Value(CTX_NOCACHE) == "1", ctx.Value(CTX_CACHEONLY) == "1"
-
- rawRev, _ := cloneCtx.Value(CTX_REQUEST_REVISION).(string)
- reqRev, reqCount := ParseRevision(rawRev)
- if !noCache && !cacheOnly && len(rawRev) > 0 {
- // force to find in cache at first time when rev is not empty
- util.SetContext(cloneCtx, CTX_CACHEONLY, "1")
- }
-
- var (
- maxRev int64
- instCount int64
- kvs []*mvccpb.KeyValue
- )
- for i := 0; i < 2; i++ {
- maxRev = 0
- kvs = kvs[:0]
-
- for _, serviceId := range ids {
- key := apt.GenerateInstanceKey(domainProject, serviceId, "")
- opts := append(FromContext(cloneCtx), registry.WithStrKey(key), registry.WithPrefix())
- resp, err := backend.Store().Instance().Search(cloneCtx, opts...)
- if err != nil {
- return nil, "", err
- }
-
- if len(resp.Kvs) == 0 {
- continue
- }
-
- if cmax := resp.MaxModRevision(); maxRev < cmax {
- maxRev = cmax
- }
- kvs = append(kvs, resp.Kvs...)
- }
-
- instCount = int64(len(kvs))
-
- if noCache || cacheOnly || len(rawRev) == 0 {
- break
- }
-
- if reqRev == maxRev && reqCount == instCount {
- // return not modified
- kvs = kvs[:0]
- break
- }
-
- if reqRev < maxRev || i != 0 {
- break
- }
-
- // find from remote server at second time
- util.SetContext(util.SetContext(cloneCtx,
- CTX_CACHEONLY, ""),
- CTX_NOCACHE, "1")
- }
-
- for _, kv := range kvs {
- instance := &pb.MicroServiceInstance{}
- err := json.Unmarshal(kv.Value, instance)
- if err != nil {
- return nil, "", fmt.Errorf("unmarshal %s faild, %s",
- util.BytesToStringWithNoCopy(kv.Key), err.Error())
- }
- instances = append(instances, instance)
- }
-
- rev = FormatRevision(maxRev, instCount)
- return
-}
-
func GetAllInstancesOfOneService(ctx context.Context, domainProject string, serviceId string) ([]*pb.MicroServiceInstance, error) {
key := apt.GenerateInstanceKey(domainProject, serviceId, "")
opts := append(FromContext(ctx), registry.WithStrKey(key), registry.WithPrefix())
@@ -165,14 +84,8 @@ func GetAllInstancesOfOneService(ctx context.Context, domainProject string, serv
}
instances := make([]*pb.MicroServiceInstance, 0, len(resp.Kvs))
- for _, kvs := range resp.Kvs {
- instance := &pb.MicroServiceInstance{}
- err := json.Unmarshal(kvs.Value, instance)
- if err != nil {
- util.Logger().Errorf(err, "Unmarshal instance of service %s failed.", serviceId)
- return nil, err
- }
- instances = append(instances, instance)
+ for _, kv := range resp.Kvs {
+ instances = append(instances, kv.Value.(*pb.MicroServiceInstance))
}
return instances, nil
}
@@ -251,7 +164,7 @@ func DeleteServiceAllInstances(ctx context.Context, serviceId string) error {
return nil
}
for _, v := range resp.Kvs {
- leaseID, _ := strconv.ParseInt(util.BytesToStringWithNoCopy(v.Value), 10, 64)
+ leaseID, _ := strconv.ParseInt(v.Value.(string), 10, 64)
backend.Registry().LeaseRevoke(ctx, leaseID)
}
return nil
@@ -271,7 +184,7 @@ func QueryAllProvidersInstances(ctx context.Context, selfServiceId string) (resu
util.Logger().Errorf(nil, "service not exist, %s", selfServiceId)
return
}
- providerIds, _, err := GetProviderIdsByConsumer(ctx, domainProject, service)
+ providerIds, _, err := GetAllProviderIds(ctx, domainProject, service)
if err != nil {
util.Logger().Errorf(err, "get service %s providers id set failed.", selfServiceId)
return
@@ -289,7 +202,6 @@ func QueryAllProvidersInstances(ctx context.Context, selfServiceId string) (resu
if service == nil {
continue
}
- util.Logger().Debugf("query provider service %v with revision %d.", service, rev)
kvs, err := queryServiceInstancesKvs(ctx, providerId, rev)
if err != nil {
@@ -298,15 +210,7 @@ func QueryAllProvidersInstances(ctx context.Context, selfServiceId string) (resu
return
}
- util.Logger().Debugf("query provider service %s instances[%d] with revision %d.", providerId, len(kvs), rev)
for _, kv := range kvs {
- instance := &pb.MicroServiceInstance{}
- err := json.Unmarshal(kv.Value, instance)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal instance of service %s with revision %d failed.",
- providerId, rev)
- return
- }
results = append(results, &pb.WatchInstanceResponse{
Response: pb.CreateResponse(pb.Response_SUCCESS, "List instance successfully."),
Action: string(pb.EVT_INIT),
@@ -316,14 +220,14 @@ func QueryAllProvidersInstances(ctx context.Context, selfServiceId string) (resu
ServiceName: service.ServiceName,
Version: service.Version,
},
- Instance: instance,
+ Instance: kv.Value.(*pb.MicroServiceInstance),
})
}
}
return
}
-func queryServiceInstancesKvs(ctx context.Context, serviceId string, rev int64) ([]*mvccpb.KeyValue, error) {
+func queryServiceInstancesKvs(ctx context.Context, serviceId string, rev int64) ([]*backend.KeyValue, error) {
domainProject := util.ParseDomainProject(ctx)
key := apt.GenerateInstanceKey(domainProject, serviceId, "")
resp, err := backend.Store().Instance().Search(ctx,
diff --git a/server/service/util/instance_util_test.go b/server/service/util/instance_util_test.go
index b99d810d..489de55a 100644
--- a/server/service/util/instance_util_test.go
+++ b/server/service/util/instance_util_test.go
@@ -24,35 +24,28 @@ import (
"testing"
)
-func TestGetLeaseId(t *testing.T) {
-
- _, err := GetLeaseId(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "", "")
- if err != nil {
- t.Fatalf(`GetLeaseId WithCacheOnly failed`)
+func TestFormatRevision(t *testing.T) {
+ if "1.1" != FormatRevision(1, 1) {
+ t.Fatalf("TestFormatRevision failed")
+ }
+ if a, b := ParseRevision("1.1"); a != 1 || b != 1 {
+ t.Fatalf("TestFormatRevision failed")
}
+}
- _, err = GetLeaseId(context.Background(), "", "", "")
+func TestGetLeaseId(t *testing.T) {
+ _, err := GetLeaseId(context.Background(), "", "", "")
if err == nil {
t.Fatalf(`GetLeaseId failed`)
}
}
func TestGetInstance(t *testing.T) {
- _, err := GetInstance(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "", "")
- if err != nil {
- t.Fatalf(`GetInstance WithCacheOnly failed`)
- }
-
- _, err = GetInstance(context.Background(), "", "", "")
+ _, err := GetInstance(context.Background(), "", "", "")
if err == nil {
t.Fatalf(`GetInstance failed`)
}
- _, err = GetAllInstancesOfOneService(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
- if err != nil {
- t.Fatalf(`GetAllInstancesOfOneService WithCacheOnly failed`)
- }
-
_, err = GetAllInstancesOfOneService(context.Background(), "", "")
if err == nil {
t.Fatalf(`GetAllInstancesOfOneService failed`)
@@ -67,12 +60,7 @@ func TestGetInstance(t *testing.T) {
}
func TestInstanceExistById(t *testing.T) {
- _, err := InstanceExistById(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "", "")
- if err != nil {
- t.Fatalf(`InstanceExistById WithCacheOnly failed`)
- }
-
- _, err = InstanceExistById(context.Background(), "", "", "")
+ _, err := InstanceExistById(context.Background(), "", "", "")
if err == nil {
t.Fatalf(`InstanceExistById failed`)
}
@@ -115,31 +103,8 @@ func TestGetInstanceCountOfOneService(t *testing.T) {
}
}
-func TestGetInstanceCountOfServices(t *testing.T) {
- _, _, err := GetAllInstancesOfServices(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", []string{"1"})
- if err != nil {
- t.Fatalf(`GetAllInstancesOfServices CTX_CACHEONLY failed`)
- }
- _, _, err = GetAllInstancesOfServices(util.SetContext(context.Background(), CTX_NOCACHE, "1"), "", []string{"1"})
- if err == nil {
- t.Fatalf(`GetAllInstancesOfServices CTX_NOCACHE failed`)
- }
- _, _, err = GetAllInstancesOfServices(util.SetContext(context.Background(), CTX_REQUEST_REVISION, 1), "", []string{"1"})
- if err == nil {
- t.Fatalf(`GetAllInstancesOfServices CTX_REQUEST_REVISION failed`)
- }
- _, _, err = GetAllInstancesOfServices(context.Background(), "", []string{"1"})
- if err == nil {
- t.Fatalf(`GetAllInstancesOfServices failed`)
- }
-}
-
func TestUpdateInstance(t *testing.T) {
- err := UpdateInstance(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", &pb.MicroServiceInstance{})
- if err == nil {
- t.Fatalf(`UpdateInstance CTX_CACHEONLY failed`)
- }
- err = UpdateInstance(util.SetContext(context.Background(), CTX_NOCACHE, "1"), "", &pb.MicroServiceInstance{})
+ err := UpdateInstance(util.SetContext(context.Background(), CTX_NOCACHE, "1"), "", &pb.MicroServiceInstance{})
if err == nil {
t.Fatalf(`UpdateInstance CTX_NOCACHE failed`)
}
diff --git a/server/service/util/microservice_util.go b/server/service/util/microservice_util.go
index 6f5d0394..aba9a98f 100644
--- a/server/service/util/microservice_util.go
+++ b/server/service/util/microservice_util.go
@@ -25,7 +25,6 @@ import (
"github.com/apache/incubator-servicecomb-service-center/server/infra/quota"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
"github.com/apache/incubator-servicecomb-service-center/server/plugin"
- "github.com/coreos/etcd/mvcc/mvccpb"
"golang.org/x/net/context"
)
@@ -43,16 +42,7 @@ func GetServiceWithRev(ctx context.Context, domain string, id string, rev int64)
if len(serviceResp.Kvs) == 0 {
return nil, nil
}
- service := &pb.MicroService{}
- err = json.Unmarshal(serviceResp.Kvs[0].Value, &service)
- if err != nil {
- return nil, err
- }
- return service, nil
-}
-
-func GetServiceInCache(ctx context.Context, domain string, id string) (*pb.MicroService, error) {
- return GetService(util.SetContext(util.CloneContext(ctx), CTX_CACHEONLY, "1"), domain, id)
+ return serviceResp.Kvs[0].Value.(*pb.MicroService), nil
}
func GetService(ctx context.Context, domainProject string, serviceId string) (*pb.MicroService, error) {
@@ -65,15 +55,10 @@ func GetService(ctx context.Context, domainProject string, serviceId string) (*p
if len(serviceResp.Kvs) == 0 {
return nil, nil
}
- service := &pb.MicroService{}
- err = json.Unmarshal(serviceResp.Kvs[0].Value, &service)
- if err != nil {
- return nil, err
- }
- return service, nil
+ return serviceResp.Kvs[0].Value.(*pb.MicroService), nil
}
-func GetServicesRawData(ctx context.Context, domainProject string) ([]*mvccpb.KeyValue, error) {
+func getServicesRawData(ctx context.Context, domainProject string) ([]*backend.KeyValue, error) {
key := apt.GenerateServiceKey(domainProject, "")
opts := append(FromContext(ctx),
registry.WithStrKey(key),
@@ -85,19 +70,14 @@ func GetServicesRawData(ctx context.Context, domainProject string) ([]*mvccpb.Ke
return resp.Kvs, err
}
-func GetServicesByDomain(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
- kvs, err := GetServicesRawData(ctx, domainProject)
+func GetServicesByDomainProject(ctx context.Context, domainProject string) ([]*pb.MicroService, error) {
+ kvs, err := getServicesRawData(ctx, domainProject)
if err != nil {
return nil, err
}
services := []*pb.MicroService{}
- for _, kvs := range kvs {
- service := &pb.MicroService{}
- err := json.Unmarshal(kvs.Value, service)
- if err != nil {
- return nil, err
- }
- services = append(services, service)
+ for _, kv := range kvs {
+ services = append(services, kv.Value.(*pb.MicroService))
}
return services, nil
}
@@ -125,7 +105,7 @@ func searchServiceId(ctx context.Context, key *pb.MicroServiceKey) (string, erro
if len(resp.Kvs) == 0 {
return "", nil
}
- return util.BytesToStringWithNoCopy(resp.Kvs[0].Value), nil
+ return resp.Kvs[0].Value.(string), nil
}
func searchServiceIdFromAlias(ctx context.Context, key *pb.MicroServiceKey) (string, error) {
@@ -137,15 +117,15 @@ func searchServiceIdFromAlias(ctx context.Context, key *pb.MicroServiceKey) (str
if len(resp.Kvs) == 0 {
return "", nil
}
- return util.BytesToStringWithNoCopy(resp.Kvs[0].Value), nil
+ return resp.Kvs[0].Value.(string), nil
}
-func GetServiceAllVersions(ctx context.Context, key *pb.MicroServiceKey, alias bool) (*registry.PluginResponse, error) {
+func GetServiceAllVersions(ctx context.Context, key *pb.MicroServiceKey, alias bool) (*backend.Response, error) {
copy := *key
copy.Version = ""
var (
prefix string
- indexer *backend.Indexer
+ indexer backend.Indexer
)
if alias {
prefix = apt.GenerateServiceAliasKey(©)
@@ -211,7 +191,7 @@ func ServiceExist(ctx context.Context, domainProject string, serviceId string) b
func GetAllServiceUtil(ctx context.Context) ([]*pb.MicroService, error) {
domainProject := util.ParseDomainProject(ctx)
- services, err := GetServicesByDomain(ctx, domainProject)
+ services, err := GetServicesByDomainProject(ctx, domainProject)
if err != nil {
return nil, err
}
diff --git a/server/service/util/rule_util.go b/server/service/util/rule_util.go
index 7a04160c..246dada3 100644
--- a/server/service/util/rule_util.go
+++ b/server/service/util/rule_util.go
@@ -17,7 +17,6 @@
package util
import (
- "encoding/json"
"fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
apt "github.com/apache/incubator-servicecomb-service-center/server/core"
@@ -33,7 +32,6 @@ import (
type RuleFilter struct {
DomainProject string
- Provider *pb.MicroService
ProviderRules []*pb.ServiceRule
}
@@ -44,6 +42,10 @@ func (rf *RuleFilter) Filter(ctx context.Context, consumerId string) (bool, erro
return false, err
}
+ if len(rf.ProviderRules) == 0 {
+ return true, nil
+ }
+
tags, err := GetTagsUtils(copyCtx, rf.DomainProject, consumerId)
if err != nil {
return false, err
@@ -58,6 +60,30 @@ func (rf *RuleFilter) Filter(ctx context.Context, consumerId string) (bool, erro
return true, nil
}
+func (rf *RuleFilter) FilterAll(ctx context.Context, consumerIds []string) (allow []string, deny []string, err error) {
+ l := len(consumerIds)
+ if l == 0 || len(rf.ProviderRules) == 0 {
+ return consumerIds, nil, nil
+ }
+
+ allowIdx, denyIdx := 0, l
+ consumers := make([]string, l)
+ for _, consumerId := range consumerIds {
+ ok, err := rf.Filter(ctx, consumerId)
+ if err != nil {
+ return nil, nil, err
+ }
+ if ok {
+ consumers[allowIdx] = consumerId
+ allowIdx++
+ } else {
+ denyIdx--
+ consumers[denyIdx] = consumerId
+ }
+ }
+ return consumers[:allowIdx], consumers[denyIdx:], nil
+}
+
func GetRulesUtil(ctx context.Context, domainProject string, serviceId string) ([]*pb.ServiceRule, error) {
key := util.StringJoin([]string{
apt.GetServiceRuleRootKey(domainProject),
@@ -72,13 +98,8 @@ func GetRulesUtil(ctx context.Context, domainProject string, serviceId string) (
}
rules := []*pb.ServiceRule{}
- for _, kvs := range resp.Kvs {
- rule := &pb.ServiceRule{}
- err := json.Unmarshal(kvs.Value, rule)
- if err != nil {
- return nil, err
- }
- rules = append(rules, rule)
+ for _, kv := range resp.Kvs {
+ rules = append(rules, kv.Value.(*pb.ServiceRule))
}
return rules, nil
}
@@ -107,12 +128,7 @@ func GetServiceRuleType(ctx context.Context, domainProject string, serviceId str
if len(resp.Kvs) == 0 {
return "", 0, nil
}
- rule := &pb.ServiceRule{}
- err = json.Unmarshal(resp.Kvs[0].Value, rule)
- if err != nil {
- util.Logger().Errorf(err, "Unmarshal rule data failed.%s", err.Error())
- }
- return rule.RuleType, len(resp.Kvs), nil
+ return resp.Kvs[0].Value.(*pb.ServiceRule).RuleType, len(resp.Kvs), nil
}
func GetOneRule(ctx context.Context, domainProject, serviceId, ruleId string) (*pb.ServiceRule, error) {
@@ -123,17 +139,11 @@ func GetOneRule(ctx context.Context, domainProject, serviceId, ruleId string) (*
util.Logger().Errorf(nil, "Get rule for service failed for %s.", err.Error())
return nil, err
}
- rule := &pb.ServiceRule{}
if len(resp.Kvs) == 0 {
util.Logger().Errorf(nil, "Get rule failed, ruleId is %s.", ruleId)
return nil, nil
}
- err = json.Unmarshal(resp.Kvs[0].Value, rule)
- if err != nil {
- util.Logger().Errorf(nil, "unmarshal resp failed for %s.", err.Error())
- return nil, err
- }
- return rule, nil
+ return resp.Kvs[0].Value.(*pb.ServiceRule), nil
}
func AllowAcrossDimension(ctx context.Context, providerService *pb.MicroService, consumerService *pb.MicroService) error {
diff --git a/server/service/util/rule_util_test.go b/server/service/util/rule_util_test.go
index 32a93a1f..08607762 100644
--- a/server/service/util/rule_util_test.go
+++ b/server/service/util/rule_util_test.go
@@ -27,29 +27,29 @@ import (
func TestRuleFilter_Filter(t *testing.T) {
rf := RuleFilter{
DomainProject: "",
- Provider: &proto.MicroService{},
ProviderRules: []*proto.ServiceRule{},
}
_, err := rf.Filter(context.Background(), "")
- if err != nil {
+ if err == nil {
t.Fatalf("RuleFilter Filter failed")
}
-}
-
-func TestGetRulesUtil(t *testing.T) {
- _, err := GetRulesUtil(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
+ _, _, err = rf.FilterAll(context.Background(), []string{""})
if err != nil {
- t.Fatalf("GetRulesUtil WithCacheOnly failed")
+ t.Fatalf("RuleFilter FilterAll failed")
}
-
- _, err = GetRulesUtil(context.Background(), "", "")
+ rf.ProviderRules = []*proto.ServiceRule{
+ {},
+ }
+ _, _, err = rf.FilterAll(context.Background(), []string{""})
if err == nil {
- t.Fatalf("GetRulesUtil failed")
+ t.Fatalf("RuleFilter FilterAll failed")
}
+}
- _, err = GetOneRule(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "", "")
- if err != nil {
- t.Fatalf("GetOneRule WithCacheOnly failed")
+func TestGetRulesUtil(t *testing.T) {
+ _, err := GetRulesUtil(context.Background(), "", "")
+ if err == nil {
+ t.Fatalf("GetRulesUtil failed")
}
_, err = GetOneRule(context.Background(), "", "", "")
@@ -69,12 +69,7 @@ func TestRuleExist(t *testing.T) {
}
func TestGetServiceRuleType(t *testing.T) {
- _, _, err := GetServiceRuleType(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
- if err != nil {
- t.Fatalf("GetServiceRuleType WithCacheOnly failed")
- }
-
- _, _, err = GetServiceRuleType(context.Background(), "", "")
+ _, _, err := GetServiceRuleType(context.Background(), "", "")
if err == nil {
t.Fatalf("GetServiceRuleType failed")
}
@@ -264,39 +259,39 @@ func TestMatchRules(t *testing.T) {
}
func TestGetConsumer(t *testing.T) {
- _, _, err := GetConsumerIdsByProvider(context.Background(), "", &proto.MicroService{})
+ _, _, err := GetAllProviderIds(context.Background(), "", &proto.MicroService{})
if err == nil {
t.Fatalf("GetConsumerIdsByProvider invalid failed")
}
- _, _, err = GetConsumerIdsByProvider(context.Background(), "", &proto.MicroService{
+ _, _, err = GetAllConsumerIds(context.Background(), "", &proto.MicroService{
ServiceId: "a",
})
- if err != nil {
+ if err == nil {
t.Fatalf("GetConsumerIdsByProvider WithCacheOnly not exist service failed")
}
- _, err = GetConsumersInCache(context.Background(), "",
+ _, err = GetConsumerIds(context.Background(), "",
&proto.MicroService{
ServiceId: "a",
})
- if err != nil {
- t.Fatalf("GetConsumersInCache WithCacheOnly failed")
+ if err == nil {
+ t.Fatalf("GetConsumerIds WithCacheOnly failed")
}
}
func TestGetProvider(t *testing.T) {
- _, err := GetProvidersInCache(context.Background(), "",
+ _, err := GetProviderIds(context.Background(), "",
&proto.MicroService{
ServiceId: "a",
})
- if err != nil {
- t.Fatalf("GetProvidersInCache WithCacheOnly failed")
+ if err == nil {
+ t.Fatalf("GetProviderIds WithCacheOnly failed")
}
- _, _, err = GetProviderIdsByConsumer(context.Background(), "", &proto.MicroService{})
- if err != nil {
- t.Fatalf("GetProviderIdsByConsumer WithCacheOnly failed")
+ _, _, err = GetAllProviderIds(context.Background(), "", &proto.MicroService{})
+ if err == nil {
+ t.Fatalf("GetAllProviderIds WithCacheOnly failed")
}
}
@@ -305,9 +300,4 @@ func TestAccessible(t *testing.T) {
if err.StatusCode() != http.StatusInternalServerError {
t.Fatalf("Accessible invalid failed")
}
-
- err = Accessible(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
- if err.StatusCode() == http.StatusInternalServerError {
- t.Fatalf("Accessible WithCacheOnly failed")
- }
}
diff --git a/server/service/util/schema_util_test.go b/server/service/util/schema_util_test.go
index 7ba71463..251d0caf 100644
--- a/server/service/util/schema_util_test.go
+++ b/server/service/util/schema_util_test.go
@@ -17,19 +17,13 @@
package util
import (
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
"golang.org/x/net/context"
"testing"
)
func TestCheckSchemaInfoExist(t *testing.T) {
- _, err := CheckSchemaInfoExist(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.FailNow()
- }
-
- _, err = CheckSchemaInfoExist(context.Background(), "")
+ _, err := CheckSchemaInfoExist(context.Background(), "")
if err == nil {
- t.FailNow()
+ t.Fatalf("TestCheckSchemaInfoExist failed")
}
}
diff --git a/server/service/util/tag_util.go b/server/service/util/tag_util.go
index 19b78024..440dda29 100644
--- a/server/service/util/tag_util.go
+++ b/server/service/util/tag_util.go
@@ -57,14 +57,8 @@ func GetTagsUtils(ctx context.Context, domainProject, serviceId string) (tags ma
return tags, err
}
- l := len(resp.Kvs)
- if l != 0 {
- tags = make(map[string]string, l)
- err = json.Unmarshal(resp.Kvs[0].Value, &tags)
- if err != nil {
- util.Logger().Errorf(err, "unmarshal service %s tags file failed", key)
- return nil, err
- }
+ if len(resp.Kvs) != 0 {
+ tags = resp.Kvs[0].Value.(map[string]string)
}
return tags, nil
}
diff --git a/server/service/util/tag_util_test.go b/server/service/util/tag_util_test.go
index 20fa7d5b..023e6835 100644
--- a/server/service/util/tag_util_test.go
+++ b/server/service/util/tag_util_test.go
@@ -17,7 +17,6 @@
package util
import (
- "github.com/apache/incubator-servicecomb-service-center/pkg/util"
"golang.org/x/net/context"
"testing"
)
@@ -30,12 +29,7 @@ func TestAddTagIntoETCD(t *testing.T) {
}
func TestGetTagsUtils(t *testing.T) {
- _, err := GetTagsUtils(util.SetContext(context.Background(), CTX_CACHEONLY, "1"), "", "")
- if err != nil {
- t.Fatalf(`GetTagsUtils WithCacheOnly failed`)
- }
-
- _, err = GetTagsUtils(context.Background(), "", "")
+ _, err := GetTagsUtils(context.Background(), "", "")
if err == nil {
t.Fatalf(`GetTagsUtils failed`)
}
diff --git a/server/service/util/util_suite_test.go b/server/service/util/util_suite_test.go
index 457e085c..fb306709 100644
--- a/server/service/util/util_suite_test.go
+++ b/server/service/util/util_suite_test.go
@@ -22,12 +22,10 @@ import (
)
import (
- "fmt"
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
"github.com/apache/incubator-servicecomb-service-center/server/core/proto"
"github.com/apache/incubator-servicecomb-service-center/server/infra/registry"
serviceUtil "github.com/apache/incubator-servicecomb-service-center/server/service/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
. "github.com/onsi/ginkgo"
"github.com/onsi/ginkgo/reporters"
. "github.com/onsi/gomega"
@@ -45,61 +43,36 @@ func TestMicroservice(t *testing.T) {
}
func TestFindServiceIds(t *testing.T) {
- _, err := serviceUtil.FindServiceIds(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"),
+ _, err := serviceUtil.FindServiceIds(context.Background(),
"latest", &proto.MicroServiceKey{})
- if err != nil {
+ if err == nil {
t.FailNow()
}
- _, err = serviceUtil.FindServiceIds(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"),
+ _, err = serviceUtil.FindServiceIds(context.Background(),
"1.0.0", &proto.MicroServiceKey{})
- if err != nil {
+ if err == nil {
t.FailNow()
}
- _, err = serviceUtil.FindServiceIds(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"),
+ _, err = serviceUtil.FindServiceIds(context.Background(),
"1.0+", &proto.MicroServiceKey{Alias: "test"})
- if err != nil {
+ if err == nil {
t.FailNow()
}
}
func TestGetService(t *testing.T) {
- _, err := serviceUtil.GetService(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "", "")
- if err != nil {
- t.FailNow()
- }
-
- _, err = serviceUtil.GetService(context.Background(), "", "")
+ _, err := serviceUtil.GetService(context.Background(), "", "")
if err == nil {
t.FailNow()
}
- _, err = serviceUtil.GetServicesRawData(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.FailNow()
- }
-
- _, err = serviceUtil.GetServicesRawData(context.Background(), "")
+ _, err = serviceUtil.GetServicesByDomainProject(context.Background(), "")
if err == nil {
t.FailNow()
}
- _, err = serviceUtil.GetServicesByDomain(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.FailNow()
- }
-
- _, err = serviceUtil.GetServicesByDomain(context.Background(), "")
- if err == nil {
- t.FailNow()
- }
-
- _, err = serviceUtil.GetAllServiceUtil(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"))
- if err != nil {
- t.FailNow()
- }
-
_, err = serviceUtil.GetAllServiceUtil(context.Background())
if err == nil {
t.FailNow()
@@ -116,6 +89,15 @@ func TestGetService(t *testing.T) {
}
}
+func TestServiceExist(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.FailNow()
+ }
+ }()
+ serviceUtil.ServiceExist(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "", "")
+}
+
func TestFromContext(t *testing.T) {
ctx := context.WithValue(context.Background(), serviceUtil.CTX_NOCACHE, "1")
opts := serviceUtil.FromContext(ctx)
@@ -140,15 +122,6 @@ func TestFromContext(t *testing.T) {
}
}
-func TestServiceExist(t *testing.T) {
- defer func() {
- if r := recover(); r != nil {
- t.FailNow()
- }
- }()
- serviceUtil.ServiceExist(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "", "")
-}
-
func TestRemandQuota(t *testing.T) {
serviceUtil.RemandServiceQuota(context.Background())
serviceUtil.RemandInstanceQuota(context.Background())
@@ -162,80 +135,3 @@ func TestSetDefault(t *testing.T) {
t.Fatalf(`TestSetDefault failed`)
}
}
-
-func TestGetOneDomainProjectServiceCount(t *testing.T) {
- _, err := serviceUtil.GetOneDomainProjectServiceCount(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.Fatalf("GetOneDomainProjectServiceCount WithCacheOnly failed")
- }
-
- _, err = serviceUtil.GetOneDomainProjectServiceCount(context.Background(), "")
- if err == nil {
- t.Fatalf("GetOneDomainProjectServiceCount failed")
- }
-}
-
-func TestGetOneDomainProjectInstanceCount(t *testing.T) {
- _, err := serviceUtil.GetOneDomainProjectInstanceCount(util.SetContext(context.Background(), serviceUtil.CTX_CACHEONLY, "1"), "")
- if err != nil {
- t.Fatalf("GetOneDomainProjectInstanceCount WithCacheOnly failed")
- }
-
- _, err = serviceUtil.GetOneDomainProjectInstanceCount(context.Background(), "")
- if err == nil {
- t.Fatalf("GetOneDomainProjectInstanceCount failed")
- }
-}
-
-const VERSIONRULE_BASE = 5000
-
-func BenchmarkVersionRule_Latest_GetServicesIds(b *testing.B) {
- var kvs = make([]*mvccpb.KeyValue, VERSIONRULE_BASE)
- for i := 1; i <= VERSIONRULE_BASE; i++ {
- kvs[i-1] = &mvccpb.KeyValue{
- Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
- Value: []byte(fmt.Sprintf("%d", i)),
- }
- }
- b.N = VERSIONRULE_BASE
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- serviceUtil.VersionRule(serviceUtil.Latest).Match(kvs)
- }
- b.ReportAllocs()
- // 5000 7105020 ns/op 2180198 B/op 39068 allocs/op
-}
-
-func BenchmarkVersionRule_Range_GetServicesIds(b *testing.B) {
- var kvs = make([]*mvccpb.KeyValue, VERSIONRULE_BASE)
- for i := 1; i <= VERSIONRULE_BASE; i++ {
- kvs[i-1] = &mvccpb.KeyValue{
- Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
- Value: []byte(fmt.Sprintf("%d", i)),
- }
- }
- b.N = VERSIONRULE_BASE
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- serviceUtil.VersionRule(serviceUtil.Range).Match(kvs, fmt.Sprintf("1.%d", i), fmt.Sprintf("1.%d", i+VERSIONRULE_BASE/10))
- }
- b.ReportAllocs()
- // 5000 7244029 ns/op 2287389 B/op 39584 allocs/op
-}
-
-func BenchmarkVersionRule_AtLess_GetServicesIds(b *testing.B) {
- var kvs = make([]*mvccpb.KeyValue, VERSIONRULE_BASE)
- for i := 1; i <= VERSIONRULE_BASE; i++ {
- kvs[i-1] = &mvccpb.KeyValue{
- Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
- Value: []byte(fmt.Sprintf("%d", i)),
- }
- }
- b.N = VERSIONRULE_BASE
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- serviceUtil.VersionRule(serviceUtil.AtLess).Match(kvs, fmt.Sprintf("1.%d", i))
- }
- b.ReportAllocs()
- // 5000 11221098 ns/op 3174720 B/op 58064 allocs/op
-}
diff --git a/server/service/util/versionrule.go b/server/service/util/versionrule.go
index 13e981cc..86061b1c 100644
--- a/server/service/util/versionrule.go
+++ b/server/service/util/versionrule.go
@@ -18,19 +18,19 @@ package util
import (
"github.com/apache/incubator-servicecomb-service-center/pkg/util"
- "github.com/coreos/etcd/mvcc/mvccpb"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/backend"
"regexp"
"sort"
"strconv"
"strings"
)
-type VersionRule func(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string) []string
+type VersionRule func(sorted []string, kvs map[string]*backend.KeyValue, start, end string) []string
-func (vr VersionRule) Match(kvs []*mvccpb.KeyValue, ops ...string) []string {
+func (vr VersionRule) Match(kvs []*backend.KeyValue, ops ...string) []string {
sorter := &serviceKeySorter{
sortArr: make([]string, len(kvs)),
- kvs: make(map[string]*mvccpb.KeyValue, len(kvs)),
+ kvs: make(map[string]*backend.KeyValue, len(kvs)),
cmp: Larger,
}
for i, kv := range kvs {
@@ -54,7 +54,7 @@ func (vr VersionRule) Match(kvs []*mvccpb.KeyValue, ops ...string) []string {
type serviceKeySorter struct {
sortArr []string
- kvs map[string]*mvccpb.KeyValue
+ kvs map[string]*backend.KeyValue
cmp func(i, j string) bool
}
@@ -99,14 +99,14 @@ func LessEqual(start, end string) bool {
return !Larger(start, end)
}
-func Latest(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string) []string {
+func Latest(sorted []string, kvs map[string]*backend.KeyValue, start, end string) []string {
if len(sorted) == 0 {
return []string{}
}
- return []string{util.BytesToStringWithNoCopy(kvs[sorted[0]].Value)}
+ return []string{kvs[sorted[0]].Value.(string)}
}
-func Range(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string) []string {
+func Range(sorted []string, kvs map[string]*backend.KeyValue, start, end string) []string {
result := make([]string, len(sorted))
i, flag := 0, 0
@@ -133,13 +133,13 @@ func Range(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string)
}
}
- result[i] = util.BytesToStringWithNoCopy(kvs[k].Value)
+ result[i] = kvs[k].Value.(string)
i++
}
return result[:i]
}
-func AtLess(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string) []string {
+func AtLess(sorted []string, kvs map[string]*backend.KeyValue, start, end string) []string {
result := make([]string, len(sorted))
if len(sorted) == 0 || Larger(start, sorted[0]) {
@@ -150,12 +150,12 @@ func AtLess(sorted []string, kvs map[string]*mvccpb.KeyValue, start, end string)
if Larger(start, k) {
return result[:i]
}
- result[i] = util.BytesToStringWithNoCopy(kvs[k].Value)
+ result[i] = kvs[k].Value.(string)
}
return result[:]
}
-func ParseVersionRule(versionRule string) func(kvs []*mvccpb.KeyValue) []string {
+func ParseVersionRule(versionRule string) func(kvs []*backend.KeyValue) []string {
if len(versionRule) == 0 {
return nil
}
@@ -163,20 +163,20 @@ func ParseVersionRule(versionRule string) func(kvs []*mvccpb.KeyValue) []string
rangeIdx := strings.Index(versionRule, "-")
switch {
case versionRule == "latest":
- return func(kvs []*mvccpb.KeyValue) []string {
+ return func(kvs []*backend.KeyValue) []string {
return VersionRule(Latest).Match(kvs)
}
case versionRule[len(versionRule)-1:] == "+":
// 取最低版本及高版本集合
start := versionRule[:len(versionRule)-1]
- return func(kvs []*mvccpb.KeyValue) []string {
+ return func(kvs []*backend.KeyValue) []string {
return VersionRule(AtLess).Match(kvs, start)
}
case rangeIdx > 0:
// 取版本范围集合
start := versionRule[:rangeIdx]
end := versionRule[rangeIdx+1:]
- return func(kvs []*mvccpb.KeyValue) []string {
+ return func(kvs []*backend.KeyValue) []string {
return VersionRule(Range).Match(kvs, start, end)
}
default:
@@ -191,8 +191,11 @@ func VersionMatchRule(version string, versionRule string) bool {
return version == versionRule
}
- return len(match([]*mvccpb.KeyValue{
- {Key: util.StringToBytesWithNoCopy("/" + version)},
+ return len(match([]*backend.KeyValue{
+ {
+ Key: util.StringToBytesWithNoCopy("/" + version),
+ Value: "",
+ },
})) > 0
}
diff --git a/server/service/util/versionrule_test.go b/server/service/util/versionrule_test.go
index 3a4616ba..bd1c9b5a 100644
--- a/server/service/util/versionrule_test.go
+++ b/server/service/util/versionrule_test.go
@@ -18,27 +18,80 @@ package util
import (
"fmt"
- "github.com/coreos/etcd/mvcc/mvccpb"
+ "github.com/apache/incubator-servicecomb-service-center/server/core/backend"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sort"
"testing"
)
+const VERSIONRULE_BASE = 5000
+
+func BenchmarkVersionRule_Latest_GetServicesIds(b *testing.B) {
+ var kvs = make([]*backend.KeyValue, VERSIONRULE_BASE)
+ for i := 1; i <= VERSIONRULE_BASE; i++ {
+ kvs[i-1] = &backend.KeyValue{
+ Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
+ Value: []byte(fmt.Sprintf("%d", i)),
+ }
+ }
+ b.N = VERSIONRULE_BASE
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ VersionRule(Latest).Match(kvs)
+ }
+ b.ReportAllocs()
+ // 5000 7105020 ns/op 2180198 B/op 39068 allocs/op
+}
+
+func BenchmarkVersionRule_Range_GetServicesIds(b *testing.B) {
+ var kvs = make([]*backend.KeyValue, VERSIONRULE_BASE)
+ for i := 1; i <= VERSIONRULE_BASE; i++ {
+ kvs[i-1] = &backend.KeyValue{
+ Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
+ Value: []byte(fmt.Sprintf("%d", i)),
+ }
+ }
+ b.N = VERSIONRULE_BASE
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ VersionRule(Range).Match(kvs, fmt.Sprintf("1.%d", i), fmt.Sprintf("1.%d", i+VERSIONRULE_BASE/10))
+ }
+ b.ReportAllocs()
+ // 5000 7244029 ns/op 2287389 B/op 39584 allocs/op
+}
+
+func BenchmarkVersionRule_AtLess_GetServicesIds(b *testing.B) {
+ var kvs = make([]*backend.KeyValue, VERSIONRULE_BASE)
+ for i := 1; i <= VERSIONRULE_BASE; i++ {
+ kvs[i-1] = &backend.KeyValue{
+ Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
+ Value: []byte(fmt.Sprintf("%d", i)),
+ }
+ }
+ b.N = VERSIONRULE_BASE
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ VersionRule(AtLess).Match(kvs, fmt.Sprintf("1.%d", i))
+ }
+ b.ReportAllocs()
+ // 5000 11221098 ns/op 3174720 B/op 58064 allocs/op
+}
+
func BenchmarkParseVersionRule(b *testing.B) {
f := ParseVersionRule("latest")
- kvs := []*mvccpb.KeyValue{
+ kvs := []*backend.KeyValue{
{
Key: []byte("/service/ver/1.0.300"),
- Value: []byte("1.0.300"),
+ Value: "1.0.300",
},
{
Key: []byte("/service/ver/1.0.303"),
- Value: []byte("1.0.303"),
+ Value: "1.0.303",
},
{
Key: []byte("/service/ver/1.0.304"),
- Value: []byte("1.0.304"),
+ Value: "1.0.304",
},
}
b.ResetTimer()
@@ -57,7 +110,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.0.0", "1.0.1"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0.1"))
@@ -67,7 +120,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.0.1", "1.0.0"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0.1"))
@@ -77,7 +130,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.0.0.0", "1.0.1"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0.1"))
@@ -87,7 +140,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.0.9", "1.0.10"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0.10"))
@@ -97,7 +150,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.10", "4"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("4"))
@@ -109,7 +162,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.a", "1.0.1.a", ""}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.a"))
@@ -120,7 +173,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs := []string{"1.0", "1.0.1.32768"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0"))
@@ -128,7 +181,7 @@ var _ = Describe("Version Rule sorter", func() {
kvs = []string{"1.0", "1.0.1.32767"}
sort.Sort(&serviceKeySorter{
sortArr: kvs,
- kvs: make(map[string]*mvccpb.KeyValue),
+ kvs: make(map[string]*backend.KeyValue),
cmp: Larger,
})
Expect(kvs[0]).To(Equal("1.0.1.32767"))
@@ -138,12 +191,12 @@ var _ = Describe("Version Rule sorter", func() {
})
Describe("VersionRule", func() {
const count = 10
- var kvs = [count]*mvccpb.KeyValue{}
+ var kvs = [count]*backend.KeyValue{}
BeforeEach(func() {
for i := 1; i <= count; i++ {
- kvs[i-1] = &mvccpb.KeyValue{
+ kvs[i-1] = &backend.KeyValue{
Key: []byte(fmt.Sprintf("/service/ver/1.%d", i)),
- Value: []byte(fmt.Sprintf("%d", i)),
+ Value: fmt.Sprintf("%d", i),
}
}
})
diff --git a/server/service/watch.go b/server/service/watch.go
index 1fcefa03..b8093099 100644
--- a/server/service/watch.go
+++ b/server/service/watch.go
@@ -47,8 +47,8 @@ func (s *InstanceService) Watch(in *pb.WatchInstanceRequest, stream pb.ServiceIn
domainProject := util.ParseDomainProject(stream.Context())
watcher := nf.NewListWatcher(in.SelfServiceId, apt.GetInstanceRootKey(domainProject)+"/", nil)
err = nf.GetNotifyService().AddSubscriber(watcher)
- util.Logger().Infof("start watch instance status, watcher %s %s", watcher.Subject(), watcher.Id())
- return nf.HandleWatchJob(watcher, stream, nf.GetNotifyService().Config.NotifyTimeout)
+ util.Logger().Infof("start watch instance status, watcher %s %s", watcher.Subject(), watcher.Group())
+ return nf.HandleWatchJob(watcher, stream)
}
func (s *InstanceService) WebSocketWatch(ctx context.Context, in *pb.WatchInstanceRequest, conn *websocket.Conn) {
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
With regards,
Apache Git Services