You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by al...@apache.org on 2021/05/08 13:15:05 UTC

[dubbo-go] branch 3.0 updated: Feat: add v3router to dubbogo (#1187)

This is an automated email from the ASF dual-hosted git repository.

alexstocks pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo-go.git


The following commit(s) were added to refs/heads/3.0 by this push:
     new 76d056e  Feat: add v3router to dubbogo (#1187)
76d056e is described below

commit 76d056e7b1b36b20b8e9c76ee79b2d48d45c3a12
Author: Laurence <45...@users.noreply.github.com>
AuthorDate: Sat May 8 21:14:58 2021 +0800

    Feat: add v3router to dubbogo (#1187)
    
    * fix: add v3router
    
    * fix: fix import block
    
    * fix: code review
    
    * fix
    
    * fix: fix nil ptr bug
---
 cluster/router/v3router/dubbo_rule.go              |  61 +++++
 cluster/router/v3router/factory.go                 |  35 +++
 cluster/router/v3router/factory_test.go            |  35 +++
 .../v3router/judger/attachment_match_judger.go     |  82 +++++++
 .../judger/attachment_match_judger_test.go         |  49 ++++
 .../router/v3router/judger/bool_match_judger.go    |  37 +++
 .../v3router/judger/bool_match_judger_test.go      |  48 ++++
 .../router/v3router/judger/double_match_judger.go  |  49 ++++
 .../v3router/judger/double_match_judger_test.go    |  61 +++++
 .../v3router/judger/double_range_match_judger.go   |  39 ++++
 .../judger/double_range_match_judger_test.go       |  47 ++++
 .../v3router/judger/list_double_match_judger.go    |  44 ++++
 .../judger/list_double_match_judger_test.go        | 100 ++++++++
 .../v3router/judger/list_string_match_judger.go    |  44 ++++
 .../router/v3router/judger/method_match_judger.go  |  89 +++++++
 .../router/v3router/judger/string_match_judger.go  |  60 +++++
 .../v3router/judger/string_match_judger_test.go    |  56 +++++
 .../v3router/judger/url_label_match_judge.go       |  32 +++
 cluster/router/v3router/k8s_api/k8s.go             |  41 ++++
 .../v3router/k8s_api/listener_handler_impl.go      | 198 ++++++++++++++++
 cluster/router/v3router/k8s_crd/client.go          | 134 +++++++++++
 .../v3router/k8s_crd/deploy/unifom_router_crd.yml  |  43 ++++
 .../router/v3router/k8s_crd/listener_handler.go    |  35 +++
 cluster/router/v3router/router_chain.go            | 246 ++++++++++++++++++++
 cluster/router/v3router/router_chain_test.go       | 208 +++++++++++++++++
 cluster/router/v3router/test_file/dest_rule.yml    |  17 ++
 .../router/v3router/test_file/virtual_service.yml  | 158 +++++++++++++
 cluster/router/v3router/uniform_route.go           |  67 ++++++
 cluster/router/v3router/uniform_rule.go            | 257 +++++++++++++++++++++
 config/uniform_router_config.go                    | 231 ++++++++++++++++++
 go.mod                                             |   3 +-
 go.sum                                             |   7 +-
 test/integrate/dubbo/go-client/go.sum              |   2 +
 test/integrate/dubbo/go-server/go.sum              |   2 +
 34 files changed, 2612 insertions(+), 5 deletions(-)

diff --git a/cluster/router/v3router/dubbo_rule.go b/cluster/router/v3router/dubbo_rule.go
new file mode 100644
index 0000000..a77a13e
--- /dev/null
+++ b/cluster/router/v3router/dubbo_rule.go
@@ -0,0 +1,61 @@
+/*
+ * 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 v3router
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// nolint
+type DubboRouterRule struct {
+	uniformRules []*UniformRule
+}
+
+func newDubboRouterRule(dubboRoutes []*config.DubboRoute,
+	destinationMap map[string]map[string]string) (*DubboRouterRule, error) {
+
+	uniformRules := make([]*UniformRule, 0)
+	for _, v := range dubboRoutes {
+		uniformRule, err := newUniformRule(v, destinationMap)
+		if err != nil {
+			return nil, err
+		}
+		uniformRules = append(uniformRules, uniformRule)
+	}
+
+	return &DubboRouterRule{
+		uniformRules: uniformRules,
+	}, nil
+}
+
+func (drr *DubboRouterRule) route(invokers []protocol.Invoker, url *common.URL,
+	invocation protocol.Invocation) []protocol.Invoker {
+
+	resultInvokers := make([]protocol.Invoker, 0)
+	for _, v := range drr.uniformRules {
+		if resultInvokers = v.route(invokers, url, invocation); len(resultInvokers) == 0 {
+			continue
+		}
+		// once there is a uniformRule successfully get target invoker lists, return it
+		return resultInvokers
+	}
+	// return s empty invoker list
+	return resultInvokers
+}
diff --git a/cluster/router/v3router/factory.go b/cluster/router/v3router/factory.go
new file mode 100644
index 0000000..30b121c
--- /dev/null
+++ b/cluster/router/v3router/factory.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 v3router
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+)
+
+// UniformRouteFactory is uniform router's factory
+type UniformRouteFactory struct{}
+
+// NewUniformRouterFactory constructs a new PriorityRouterFactory
+func NewUniformRouterFactory() router.PriorityRouterFactory {
+	return &UniformRouteFactory{}
+}
+
+// NewPriorityRouter construct a new UniformRouteFactory as PriorityRouter
+func (f *UniformRouteFactory) NewPriorityRouter(vsConfigBytes, distConfigBytes []byte, notify chan struct{}) (router.PriorityRouter, error) {
+	return NewUniformRouterChain(vsConfigBytes, distConfigBytes, notify)
+}
diff --git a/cluster/router/v3router/factory_test.go b/cluster/router/v3router/factory_test.go
new file mode 100644
index 0000000..365d755
--- /dev/null
+++ b/cluster/router/v3router/factory_test.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 v3router
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+// TestUniformRouterFacotry created a new factory that can new uniform router
+func TestUniformRouterFacotry(t *testing.T) {
+	factory := NewUniformRouterFactory()
+	assert.NotNil(t, factory)
+	router, err := factory.NewPriorityRouter([]byte{}, []byte{}, make(chan struct{}))
+	assert.Nil(t, err)
+	assert.NotNil(t, router)
+}
diff --git a/cluster/router/v3router/judger/attachment_match_judger.go b/cluster/router/v3router/judger/attachment_match_judger.go
new file mode 100644
index 0000000..7772a2d
--- /dev/null
+++ b/cluster/router/v3router/judger/attachment_match_judger.go
@@ -0,0 +1,82 @@
+/*
+ * 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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+type AttachmentMatchJudger struct {
+	config.DubboAttachmentMatch
+}
+
+// nolint
+func (amj *AttachmentMatchJudger) Judge(invocation protocol.Invocation) bool {
+	invAttaMap := invocation.Attachments()
+	if amj.EagleeyeContext != nil {
+		for k, v := range amj.EagleeyeContext {
+			invAttaValue, ok := invAttaMap[k]
+			if !ok {
+				if v.Empty == "" {
+					return false
+				}
+				continue
+			}
+			// exist this key
+			str, ok := invAttaValue.(string)
+			if !ok {
+				return false
+			}
+			strJudger := NewStringMatchJudger(v)
+			if !strJudger.Judge(str) {
+				return false
+			}
+		}
+	}
+
+	if amj.DubboContext != nil {
+		for k, v := range amj.DubboContext {
+			invAttaValue, ok := invAttaMap[k]
+			if !ok {
+				if v.Empty == "" {
+					return false
+				}
+				continue
+			}
+			// exist this key
+			str, ok := invAttaValue.(string)
+			if !ok {
+				return false
+			}
+			strJudger := NewStringMatchJudger(v)
+			if !strJudger.Judge(str) {
+				return false
+			}
+		}
+	}
+
+	return true
+}
+
+// nolint
+func NewAttachmentMatchJudger(matchConf *config.DubboAttachmentMatch) *AttachmentMatchJudger {
+	return &AttachmentMatchJudger{
+		DubboAttachmentMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/attachment_match_judger_test.go b/cluster/router/v3router/judger/attachment_match_judger_test.go
new file mode 100644
index 0000000..47dae98
--- /dev/null
+++ b/cluster/router/v3router/judger/attachment_match_judger_test.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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+func TestAttachmentMatchJudger(t *testing.T) {
+	dubboCtxMap := make(map[string]*config.StringMatch)
+	dubboIvkMap := make(map[string]interface{})
+	dubboCtxMap["test-key"] = &config.StringMatch{
+		Exact: "abc",
+	}
+	dubboIvkMap["test-key"] = "abc"
+	assert.True(t, NewAttachmentMatchJudger(&config.DubboAttachmentMatch{
+		DubboContext: dubboCtxMap,
+	}).Judge(invocation.NewRPCInvocation("method", nil, dubboIvkMap)))
+
+	dubboIvkMap["test-key"] = "abd"
+	assert.False(t, NewAttachmentMatchJudger(&config.DubboAttachmentMatch{
+		DubboContext: dubboCtxMap,
+	}).Judge(invocation.NewRPCInvocation("method", nil, dubboIvkMap)))
+
+}
diff --git a/cluster/router/v3router/judger/bool_match_judger.go b/cluster/router/v3router/judger/bool_match_judger.go
new file mode 100644
index 0000000..da6bf8a
--- /dev/null
+++ b/cluster/router/v3router/judger/bool_match_judger.go
@@ -0,0 +1,37 @@
+/*
+ * 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 judger
+
+import "github.com/apache/dubbo-go/config"
+
+// nolint
+type BoolMatchJudger struct {
+	config.BoolMatch
+}
+
+// nolint
+func (lsmj *BoolMatchJudger) Judge(input bool) bool {
+	return input == lsmj.Exact
+}
+
+// nolint
+func newBoolMatchJudger(matchConf *config.BoolMatch) *BoolMatchJudger {
+	return &BoolMatchJudger{
+		BoolMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/bool_match_judger_test.go b/cluster/router/v3router/judger/bool_match_judger_test.go
new file mode 100644
index 0000000..1ed3e35
--- /dev/null
+++ b/cluster/router/v3router/judger/bool_match_judger_test.go
@@ -0,0 +1,48 @@
+/*
+ * 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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func TestBoolMatchJudger(t *testing.T) {
+	assert.True(t, newBoolMatchJudger(&config.BoolMatch{
+		Exact: true,
+	}).Judge(true))
+
+	assert.True(t, newBoolMatchJudger(&config.BoolMatch{
+		Exact: false,
+	}).Judge(false))
+
+	assert.False(t, newBoolMatchJudger(&config.BoolMatch{
+		Exact: true,
+	}).Judge(false))
+
+	assert.False(t, newBoolMatchJudger(&config.BoolMatch{
+		Exact: false,
+	}).Judge(true))
+}
diff --git a/cluster/router/v3router/judger/double_match_judger.go b/cluster/router/v3router/judger/double_match_judger.go
new file mode 100644
index 0000000..b640d94
--- /dev/null
+++ b/cluster/router/v3router/judger/double_match_judger.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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+// nolint
+type DoubleMatchJudger struct {
+	config.DoubleMatch
+}
+
+// nolint
+func (dmj *DoubleMatchJudger) Judge(input float64) bool {
+	if dmj.Exact != 0 {
+		return input == dmj.Exact
+	}
+	if dmj.Range != nil {
+		return newDoubleRangeMatchJudger(dmj.Range).Judge(input)
+	}
+	// todo  mod  match ??
+	//if dmj.Mode != 0 {
+	//
+	//}
+	return true
+}
+
+// nolint
+func newDoubleMatchJudger(matchConf *config.DoubleMatch) *DoubleMatchJudger {
+	return &DoubleMatchJudger{
+		DoubleMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/double_match_judger_test.go b/cluster/router/v3router/judger/double_match_judger_test.go
new file mode 100644
index 0000000..4864b50
--- /dev/null
+++ b/cluster/router/v3router/judger/double_match_judger_test.go
@@ -0,0 +1,61 @@
+/*
+ * 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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func TestDoubleMatchJudger(t *testing.T) {
+	assert.True(t, newDoubleMatchJudger(&config.DoubleMatch{
+		Exact: 3.14159,
+	}).Judge(3.14159))
+
+	assert.False(t, newDoubleMatchJudger(&config.DoubleMatch{
+		Exact: 3.14159,
+	}).Judge(3.14155927))
+
+	assert.True(t, newDoubleMatchJudger(&config.DoubleMatch{
+		Range: &config.DoubleRangeMatch{
+			Start: 1.0,
+			End:   1.5,
+		},
+	}).Judge(1.3))
+
+	assert.False(t, newDoubleMatchJudger(&config.DoubleMatch{
+		Range: &config.DoubleRangeMatch{
+			Start: 1.0,
+			End:   1.5,
+		},
+	}).Judge(1.9))
+
+	assert.False(t, newDoubleMatchJudger(&config.DoubleMatch{
+		Range: &config.DoubleRangeMatch{
+			Start: 1.0,
+			End:   1.5,
+		},
+	}).Judge(0.9))
+}
diff --git a/cluster/router/v3router/judger/double_range_match_judger.go b/cluster/router/v3router/judger/double_range_match_judger.go
new file mode 100644
index 0000000..3f3adc3
--- /dev/null
+++ b/cluster/router/v3router/judger/double_range_match_judger.go
@@ -0,0 +1,39 @@
+/*
+ * 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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+// nolint
+type DoubleRangeMatchJudger struct {
+	config.DoubleRangeMatch
+}
+
+// nolint
+func (drmj *DoubleRangeMatchJudger) Judge(input float64) bool {
+	return input >= drmj.Start && input < drmj.End
+}
+
+// nolint
+func newDoubleRangeMatchJudger(matchConf *config.DoubleRangeMatch) *DoubleRangeMatchJudger {
+	return &DoubleRangeMatchJudger{
+		DoubleRangeMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/double_range_match_judger_test.go b/cluster/router/v3router/judger/double_range_match_judger_test.go
new file mode 100644
index 0000000..4e1926d
--- /dev/null
+++ b/cluster/router/v3router/judger/double_range_match_judger_test.go
@@ -0,0 +1,47 @@
+/*
+ * 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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func TestDoubleRangeMatchJudger(t *testing.T) {
+	assert.True(t, newDoubleRangeMatchJudger(&config.DoubleRangeMatch{
+		Start: 1.0,
+		End:   1.5,
+	}).Judge(1.3))
+
+	assert.False(t, newDoubleRangeMatchJudger(&config.DoubleRangeMatch{
+		Start: 1.0,
+		End:   1.5,
+	}).Judge(1.9))
+
+	assert.False(t, newDoubleRangeMatchJudger(&config.DoubleRangeMatch{
+		Start: 1.0,
+		End:   1.5,
+	}).Judge(0.9))
+}
diff --git a/cluster/router/v3router/judger/list_double_match_judger.go b/cluster/router/v3router/judger/list_double_match_judger.go
new file mode 100644
index 0000000..25feccd
--- /dev/null
+++ b/cluster/router/v3router/judger/list_double_match_judger.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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+// nolint
+type ListDoubleMatchJudger struct {
+	config.ListDoubleMatch
+}
+
+// nolint
+func (lsmj *ListDoubleMatchJudger) Judge(input float64) bool {
+	for _, v := range lsmj.Oneof {
+		if newDoubleMatchJudger(v).Judge(input) {
+			return true
+		}
+	}
+	return false
+}
+
+// nolint
+func newListDoubleMatchJudger(matchConf *config.ListDoubleMatch) *ListDoubleMatchJudger {
+	return &ListDoubleMatchJudger{
+		ListDoubleMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/list_double_match_judger_test.go b/cluster/router/v3router/judger/list_double_match_judger_test.go
new file mode 100644
index 0000000..66d884f
--- /dev/null
+++ b/cluster/router/v3router/judger/list_double_match_judger_test.go
@@ -0,0 +1,100 @@
+/*
+ * 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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func TestListDoubleMatchJudger_Judge(t *testing.T) {
+	assert.True(t, newListDoubleMatchJudger(&config.ListDoubleMatch{
+		Oneof: []*config.DoubleMatch{
+			{
+				Exact: 3.14,
+			},
+			{
+				Range: &config.DoubleRangeMatch{
+					Start: 1.5,
+					End:   1.9,
+				},
+			},
+			{
+				Exact: 1.3,
+			},
+		},
+	}).Judge(1.3))
+
+	assert.False(t, newListDoubleMatchJudger(&config.ListDoubleMatch{
+		Oneof: []*config.DoubleMatch{
+			{
+				Exact: 3.14,
+			},
+			{
+				Range: &config.DoubleRangeMatch{
+					Start: 1.5,
+					End:   1.9,
+				},
+			},
+			{
+				Exact: 1.2,
+			},
+		},
+	}).Judge(1.3))
+
+	assert.True(t, newListDoubleMatchJudger(&config.ListDoubleMatch{
+		Oneof: []*config.DoubleMatch{
+			{
+				Exact: 3.14,
+			},
+			{
+				Range: &config.DoubleRangeMatch{
+					Start: 1.2,
+					End:   1.9,
+				},
+			},
+			{
+				Exact: 1.4,
+			},
+		},
+	}).Judge(1.3))
+
+	assert.False(t, newListDoubleMatchJudger(&config.ListDoubleMatch{
+		Oneof: []*config.DoubleMatch{
+			{
+				Exact: 3.14,
+			},
+			{
+				Range: &config.DoubleRangeMatch{
+					Start: 1.5,
+					End:   1.9,
+				},
+			},
+			{
+				Exact: 1.0,
+			},
+		},
+	}).Judge(1.3))
+}
diff --git a/cluster/router/v3router/judger/list_string_match_judger.go b/cluster/router/v3router/judger/list_string_match_judger.go
new file mode 100644
index 0000000..e4f7cb7
--- /dev/null
+++ b/cluster/router/v3router/judger/list_string_match_judger.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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+// nolint
+type ListStringMatchJudger struct {
+	config.ListStringMatch
+}
+
+// nolint
+func (lsmj *ListStringMatchJudger) Judge(input string) bool {
+	for _, v := range lsmj.Oneof {
+		if NewStringMatchJudger(v).Judge(input) {
+			return true
+		}
+	}
+	return false
+}
+
+// nolint
+func newListStringMatchJudger(matchConf *config.ListStringMatch) *ListStringMatchJudger {
+	return &ListStringMatchJudger{
+		ListStringMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/method_match_judger.go b/cluster/router/v3router/judger/method_match_judger.go
new file mode 100644
index 0000000..672a43a
--- /dev/null
+++ b/cluster/router/v3router/judger/method_match_judger.go
@@ -0,0 +1,89 @@
+/*
+ * 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 judger
+
+import (
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// nolint
+type MethodMatchJudger struct {
+	config.DubboMethodMatch
+}
+
+// Judge Method Match Judger only judge on
+func (mmj *MethodMatchJudger) Judge(invocation protocol.Invocation) bool {
+	if mmj.NameMatch != nil {
+		strJudger := NewStringMatchJudger(mmj.NameMatch)
+		if !strJudger.Judge(invocation.MethodName()) {
+			return false
+		}
+	}
+
+	// todo now argc Must not be zero, else it will cause unexpected result
+	if mmj.Argc != 0 && len(invocation.ParameterValues()) != mmj.Argc {
+		return false
+	}
+
+	if mmj.Args != nil {
+		params := invocation.ParameterValues()
+		for _, v := range mmj.Args {
+			index := int(v.Index)
+			if index > len(params) || index < 1 {
+				return false
+			}
+			value := params[index-1]
+			if value.Type().String() != v.Type {
+				return false
+			}
+			switch v.Type {
+			case "string":
+				if !newListStringMatchJudger(v.StrValue).Judge(value.String()) {
+					return false
+				}
+			case "float", "int":
+				// todo now numbers Must not be zero, else it will ignore this match
+				if !newListDoubleMatchJudger(v.NumValue).Judge(value.Float()) {
+					return false
+				}
+			case "bool":
+				if !newBoolMatchJudger(v.BoolValue).Judge(value.Bool()) {
+					return false
+				}
+			default:
+			}
+		}
+	}
+	// todo Argp match judge ??? conflict to args?
+	//if mmj.Argp != nil {
+	//
+	//}
+	// todo Headers match judge: reserve for triple
+	//if mmj.Headers != nil {
+	//
+	//}
+	return true
+}
+
+// nolint
+func NewMethodMatchJudger(matchConf *config.DubboMethodMatch) *MethodMatchJudger {
+	return &MethodMatchJudger{
+		DubboMethodMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/string_match_judger.go b/cluster/router/v3router/judger/string_match_judger.go
new file mode 100644
index 0000000..02b74b5
--- /dev/null
+++ b/cluster/router/v3router/judger/string_match_judger.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 judger
+
+import (
+	"regexp"
+	"strings"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+// nolint
+type StringMatchJudger struct {
+	config.StringMatch
+}
+
+// nolint
+func (smj *StringMatchJudger) Judge(input string) bool {
+	if smj.Exact != "" {
+		return input == smj.Exact
+	}
+	if smj.Prefix != "" {
+		return strings.HasPrefix(input, smj.Prefix)
+	}
+	if smj.Regex != "" {
+		ok, err := regexp.MatchString(smj.Regex, input)
+		return ok && err == nil
+	}
+	if smj.NoEmpty != "" {
+		return input != ""
+	}
+	if smj.Empty != "" {
+		return input == ""
+	}
+	return true
+}
+
+// nolint
+func NewStringMatchJudger(matchConf *config.StringMatch) *StringMatchJudger {
+	return &StringMatchJudger{
+		StringMatch: *matchConf,
+	}
+}
diff --git a/cluster/router/v3router/judger/string_match_judger_test.go b/cluster/router/v3router/judger/string_match_judger_test.go
new file mode 100644
index 0000000..81c25e9
--- /dev/null
+++ b/cluster/router/v3router/judger/string_match_judger_test.go
@@ -0,0 +1,56 @@
+/*
+ * 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 judger
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/config"
+)
+
+func TestNewStringMatchJudger(t *testing.T) {
+	assert.True(t, NewStringMatchJudger(&config.StringMatch{
+		Exact: "abc",
+	}).Judge("abc"))
+
+	assert.False(t, NewStringMatchJudger(&config.StringMatch{
+		Exact: "abcd",
+	}).Judge("abc"))
+
+	assert.True(t, NewStringMatchJudger(&config.StringMatch{
+		Prefix: "abc",
+	}).Judge("abcd"))
+
+	assert.False(t, NewStringMatchJudger(&config.StringMatch{
+		Exact: "abcd",
+	}).Judge("abdc"))
+
+	assert.True(t, NewStringMatchJudger(&config.StringMatch{
+		Empty: "true",
+	}).Judge(""))
+
+	assert.False(t, NewStringMatchJudger(&config.StringMatch{
+		NoEmpty: "true",
+	}).Judge(""))
+}
diff --git a/cluster/router/v3router/judger/url_label_match_judge.go b/cluster/router/v3router/judger/url_label_match_judge.go
new file mode 100644
index 0000000..bfb26e4
--- /dev/null
+++ b/cluster/router/v3router/judger/url_label_match_judge.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 judger
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+// nolint
+func JudgeUrlLabel(url *common.URL, labels map[string]string) bool {
+	for k, v := range labels {
+		if url.GetParam(k, "") != v {
+			return false
+		}
+	}
+	return true
+}
diff --git a/cluster/router/v3router/k8s_api/k8s.go b/cluster/router/v3router/k8s_api/k8s.go
new file mode 100644
index 0000000..75e8a21
--- /dev/null
+++ b/cluster/router/v3router/k8s_api/k8s.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 k8s_api
+
+import (
+	"github.com/apache/dubbo-go/cluster/router/v3router/k8s_crd"
+	"github.com/apache/dubbo-go/config_center"
+)
+
+const (
+	GroupName    = "service.dubbo.apache.org"
+	GroupVersion = "v1alpha1"
+	Namespace    = "dubbo-workplace"
+)
+
+func SetK8sEventListener(listener config_center.ConfigurationListener) error {
+	vsUniformRouterListenerHandler := newVirtualServiceListenerHandler(listener)
+	drUniformRouterListenerHandler := newDestRuleListenerHandler(listener)
+	k8sCRDClient, err := k8s_crd.NewK8sCRDClient(GroupName, GroupVersion, Namespace,
+		vsUniformRouterListenerHandler, drUniformRouterListenerHandler)
+	if err != nil {
+		return err
+	}
+	k8sCRDClient.WatchResources()
+	return nil
+}
diff --git a/cluster/router/v3router/k8s_api/listener_handler_impl.go b/cluster/router/v3router/k8s_api/listener_handler_impl.go
new file mode 100644
index 0000000..b2f0206
--- /dev/null
+++ b/cluster/router/v3router/k8s_api/listener_handler_impl.go
@@ -0,0 +1,198 @@
+/*
+ * 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 k8s_api
+
+import (
+	metav "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/watch"
+	"k8s.io/client-go/kubernetes/scheme"
+	"k8s.io/client-go/rest"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router/v3router/k8s_crd"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+const (
+	VirtualServiceEventKey  = "virtualServiceEventKey"
+	DestinationRuleEventKey = "destinationRuleEventKe3y"
+
+	VirtualServiceResource = "virtualservices"
+	DestRuleResource       = "destinationrules"
+)
+
+// nolint
+type VirtualServiceListenerHandler struct {
+	listener config_center.ConfigurationListener
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) AddFunc(obj interface{}) {
+	if vsc, ok := obj.(*config.VirtualServiceConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        VirtualServiceEventKey,
+			Value:      vsc,
+			ConfigType: remoting.EventTypeAdd,
+		}
+		r.listener.Process(event)
+	}
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) UpdateFunc(oldObj, newObj interface{}) {
+	if vsc, ok := newObj.(*config.VirtualServiceConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        VirtualServiceEventKey,
+			Value:      vsc,
+			ConfigType: remoting.EventTypeUpdate,
+		}
+		r.listener.Process(event)
+	}
+
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) DeleteFunc(obj interface{}) {
+	if vsc, ok := obj.(*config.VirtualServiceConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        VirtualServiceEventKey,
+			Value:      vsc,
+			ConfigType: remoting.EventTypeDel,
+		}
+		r.listener.Process(event)
+	}
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) Watch(opts metav.ListOptions, restClient *rest.RESTClient, ns string) (watch.Interface, error) {
+	opts.Watch = true
+	return restClient.
+		Get().
+		Namespace(ns).
+		Resource(VirtualServiceResource).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Watch()
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) List(opts metav.ListOptions, restClient *rest.RESTClient, ns string) (runtime.Object, error) {
+	result := config.VirtualServiceConfigList{}
+	err := restClient.
+		Get().
+		Namespace(ns).
+		Resource(VirtualServiceResource).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Do().
+		Into(&result)
+
+	return &result, err
+}
+
+// nolint
+func (r *VirtualServiceListenerHandler) GetObject() runtime.Object {
+	return &config.VirtualServiceConfig{}
+}
+
+// nolint
+func newVirtualServiceListenerHandler(listener config_center.ConfigurationListener) k8s_crd.ListenerHandler {
+	return &VirtualServiceListenerHandler{
+		listener: listener,
+	}
+}
+
+// nolint
+type DestRuleListenerHandler struct {
+	listener config_center.ConfigurationListener
+}
+
+// nolint
+func (r *DestRuleListenerHandler) AddFunc(obj interface{}) {
+	if drc, ok := obj.(*config.DestinationRuleConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        DestinationRuleEventKey,
+			Value:      drc,
+			ConfigType: remoting.EventTypeAdd,
+		}
+		r.listener.Process(event)
+	}
+
+}
+
+// nolint
+func (r *DestRuleListenerHandler) UpdateFunc(oldObj, newObj interface{}) {
+	if drc, ok := newObj.(*config.DestinationRuleConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        DestinationRuleEventKey,
+			Value:      drc,
+			ConfigType: remoting.EventTypeUpdate,
+		}
+		r.listener.Process(event)
+	}
+}
+
+// nolint
+func (r *DestRuleListenerHandler) DeleteFunc(obj interface{}) {
+	if drc, ok := obj.(*config.DestinationRuleConfig); ok {
+		event := &config_center.ConfigChangeEvent{
+			Key:        DestinationRuleEventKey,
+			Value:      drc,
+			ConfigType: remoting.EventTypeDel,
+		}
+		r.listener.Process(event)
+	}
+}
+
+// nolint
+func (r *DestRuleListenerHandler) Watch(opts metav.ListOptions, restClient *rest.RESTClient, ns string) (watch.Interface, error) {
+	opts.Watch = true
+	return restClient.
+		Get().
+		Namespace(ns).
+		Resource(DestRuleResource).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Watch()
+}
+
+// nolint
+func (r *DestRuleListenerHandler) List(opts metav.ListOptions, restClient *rest.RESTClient, ns string) (runtime.Object, error) {
+	result := config.DestinationRuleConfigList{}
+	err := restClient.
+		Get().
+		Namespace(ns).
+		Resource(DestRuleResource).
+		VersionedParams(&opts, scheme.ParameterCodec).
+		Do().
+		Into(&result)
+
+	return &result, err
+}
+
+// nolint
+func (r *DestRuleListenerHandler) GetObject() runtime.Object {
+	return &config.DestinationRuleConfig{}
+}
+
+func newDestRuleListenerHandler(listener config_center.ConfigurationListener) k8s_crd.ListenerHandler {
+	return &DestRuleListenerHandler{
+		listener: listener,
+	}
+}
diff --git a/cluster/router/v3router/k8s_crd/client.go b/cluster/router/v3router/k8s_crd/client.go
new file mode 100644
index 0000000..5634d68
--- /dev/null
+++ b/cluster/router/v3router/k8s_crd/client.go
@@ -0,0 +1,134 @@
+/*
+ * 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 k8s_crd
+
+import (
+	"sync"
+	"time"
+)
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/runtime/schema"
+	"k8s.io/apimachinery/pkg/runtime/serializer"
+	"k8s.io/apimachinery/pkg/util/wait"
+	"k8s.io/apimachinery/pkg/watch"
+	"k8s.io/client-go/kubernetes/scheme"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/cache"
+)
+
+import (
+	"github.com/apache/dubbo-go/common/logger"
+)
+
+type Client struct {
+	rawClient           *rest.RESTClient
+	groupVersion        schema.GroupVersion
+	namespace           string
+	listenerHandlerList []ListenerHandler
+
+	once sync.Once
+}
+
+func (c *Client) addKnownTypes(scheme *runtime.Scheme) error {
+	for _, v := range c.listenerHandlerList {
+		scheme.AddKnownTypes(c.groupVersion,
+			v.GetObject(),
+		)
+	}
+
+	metav1.AddToGroupVersion(scheme, c.groupVersion)
+	return nil
+}
+
+// NewK8sCRDClient create an K8sCRD client, for target  CRD objects:  @objects
+// with given @groupname, @groupVersion, @namespace
+// list and watchFunction would be called by k8s informer
+func NewK8sCRDClient(groupName, groupVersion, namespace string, handlers ...ListenerHandler) (*Client, error) {
+	var config *rest.Config
+	var err error
+
+	config, err = rest.InClusterConfig()
+	if err != nil {
+		logger.Warn("InClusterConfig failed, can't get uniform router config from k8s")
+		return nil, err
+	}
+
+	newClient := &Client{
+		listenerHandlerList: handlers,
+		namespace:           namespace,
+		groupVersion:        schema.GroupVersion{Group: groupName, Version: groupVersion},
+	}
+
+	// register object
+	SchemeBuilder := runtime.NewSchemeBuilder(newClient.addKnownTypes)
+
+	// add to scheme
+	if err = SchemeBuilder.AddToScheme(scheme.Scheme); err != nil {
+		logger.Error("AddToScheme failed in k8s CRD process")
+		return nil, err
+	}
+
+	// init crd config
+	crdConfig := *config
+	crdConfig.ContentConfig.GroupVersion = &newClient.groupVersion
+	crdConfig.APIPath = "/apis"
+	crdConfig.NegotiatedSerializer = serializer.NewCodecFactory(scheme.Scheme)
+	crdConfig.UserAgent = rest.DefaultKubernetesUserAgent()
+
+	newRestClient, err := rest.UnversionedRESTClientFor(&crdConfig)
+	if err != nil {
+		logger.Error("InClusterConfig failed, can't get uniform router config from k8s")
+		return nil, err
+	}
+	newClient.rawClient = newRestClient
+	return newClient, nil
+}
+
+// func (c *Client) WatchResources() []cache.Store { can only be called once
+func (c *Client) WatchResources() []cache.Store {
+	stores := make([]cache.Store, 0)
+	c.once.Do(
+		func() {
+			for _, h := range c.listenerHandlerList {
+				projectStore, projectController := cache.NewInformer(
+					&cache.ListWatch{
+						ListFunc: func(lo metav1.ListOptions) (result runtime.Object, err error) {
+							return h.List(lo, c.rawClient, c.namespace)
+						},
+						WatchFunc: func(lo metav1.ListOptions) (watch.Interface, error) {
+							return h.Watch(lo, c.rawClient, c.namespace)
+						},
+					},
+					h.GetObject(),
+					time.Second*30, //todo can be configured
+					cache.ResourceEventHandlerFuncs{
+						AddFunc:    h.AddFunc,
+						UpdateFunc: h.UpdateFunc,
+						DeleteFunc: h.DeleteFunc,
+					},
+				)
+
+				go projectController.Run(wait.NeverStop)
+				stores = append(stores, projectStore)
+			}
+		})
+	return stores
+}
diff --git a/cluster/router/v3router/k8s_crd/deploy/unifom_router_crd.yml b/cluster/router/v3router/k8s_crd/deploy/unifom_router_crd.yml
new file mode 100644
index 0000000..9acc7d7
--- /dev/null
+++ b/cluster/router/v3router/k8s_crd/deploy/unifom_router_crd.yml
@@ -0,0 +1,43 @@
+apiVersion: "apiextensions.k8s.io/v1"
+kind: "CustomResourceDefinition"
+metadata:
+  name: "virtualservices.service.dubbo.apache.org"
+spec:
+  group: "service.dubbo.apache.org"
+  versions:
+    - name: "v1alpha1"
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+  scope: "Namespaced"
+  names:
+    plural: "virtualservices"
+    singular: "virtualservice"
+    kind: "VirtualService"
+---
+apiVersion: "apiextensions.k8s.io/v1"
+kind: "CustomResourceDefinition"
+metadata:
+  name: "destinationrules.service.dubbo.apache.org"
+spec:
+  group: "service.dubbo.apache.org"
+  versions:
+    - name: "v1alpha1"
+      served: true
+      storage: true
+      schema:
+        openAPIV3Schema:
+          type: object
+          properties:
+            spec:
+              type: object
+  scope: "Namespaced"
+  names:
+    plural: "destinationrules"
+    singular: "destinationrule"
+    kind: "DestinationRule"
\ No newline at end of file
diff --git a/cluster/router/v3router/k8s_crd/listener_handler.go b/cluster/router/v3router/k8s_crd/listener_handler.go
new file mode 100644
index 0000000..2a52291
--- /dev/null
+++ b/cluster/router/v3router/k8s_crd/listener_handler.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 k8s_crd
+
+import (
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/apimachinery/pkg/watch"
+	"k8s.io/client-go/rest"
+)
+
+// nolint
+type ListenerHandler interface {
+	AddFunc(obj interface{})
+	UpdateFunc(oldObj interface{}, newObj interface{})
+	DeleteFunc(obj interface{})
+	Watch(opts v1.ListOptions, restClient *rest.RESTClient, ns string) (watch.Interface, error)
+	List(opts v1.ListOptions, restClient *rest.RESTClient, ns string) (runtime.Object, error)
+	GetObject() runtime.Object
+}
diff --git a/cluster/router/v3router/router_chain.go b/cluster/router/v3router/router_chain.go
new file mode 100644
index 0000000..4b03c1b
--- /dev/null
+++ b/cluster/router/v3router/router_chain.go
@@ -0,0 +1,246 @@
+/*
+ * 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 v3router
+
+import (
+	"encoding/json"
+	"io"
+	"strings"
+)
+
+import (
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/cluster/router/v3router/k8s_api"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/remoting"
+)
+
+// RouterChain contains all uniform router logic
+// it has UniformRouter list,
+type RouterChain struct {
+	routers                    []*UniformRouter
+	virtualServiceConfigBytes  []byte
+	destinationRuleConfigBytes []byte
+	notify                     chan struct{}
+}
+
+// NewUniformRouterChain return
+func NewUniformRouterChain(virtualServiceConfig, destinationRuleConfig []byte, notify chan struct{}) (router.PriorityRouter, error) {
+	fromFileConfig := true
+	uniformRouters, err := parseFromConfigToRouters(virtualServiceConfig, destinationRuleConfig, notify)
+	if err != nil {
+		fromFileConfig = false
+		logger.Warnf("parse router config form local file failed, error = %+v", err)
+	}
+	r := &RouterChain{
+		virtualServiceConfigBytes:  virtualServiceConfig,
+		destinationRuleConfigBytes: destinationRuleConfig,
+		routers:                    uniformRouters,
+		notify:                     notify,
+	}
+	if err := k8s_api.SetK8sEventListener(r); err != nil {
+		logger.Warnf("try listen K8s router config failed, error = %+v", err)
+		if !fromFileConfig {
+			panic("No config file from both local file and k8s")
+		}
+	}
+	return r, nil
+}
+
+// Route route invokers using RouterChain's routers one by one
+func (r *RouterChain) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	for _, v := range r.routers {
+		invokers = v.Route(invokers, url, invocation)
+	}
+	return invokers
+}
+
+func (r *RouterChain) Process(event *config_center.ConfigChangeEvent) {
+	logger.Debugf("on processed event = %+v\n", *event)
+	if event.ConfigType == remoting.EventTypeAdd || event.ConfigType == remoting.EventTypeUpdate {
+		switch event.Key {
+		case k8s_api.VirtualServiceEventKey:
+			logger.Debug("virtul service event")
+			newVSValue, ok := event.Value.(*config.VirtualServiceConfig)
+			if !ok {
+				logger.Error("event.Value assertion error")
+				return
+			}
+
+			newVSJsonValue, ok := newVSValue.ObjectMeta.Annotations["kubectl.kubernetes.io/last-applied-configuration"]
+			if !ok {
+				logger.Error("newVSValue.ObjectMeta.Annotations has no key named kubectl.kubernetes.io/last-applied-configuration")
+				return
+			}
+			logger.Debugf("json file = %v\n", newVSJsonValue)
+			newVirtualServiceConfig := &config.VirtualServiceConfig{}
+			if err := json.Unmarshal([]byte(newVSJsonValue), newVirtualServiceConfig); err != nil {
+				logger.Error("on process json data unmarshal error = ", err)
+				return
+			}
+			newVirtualServiceConfig.YamlAPIVersion = newVirtualServiceConfig.APIVersion
+			newVirtualServiceConfig.YamlKind = newVirtualServiceConfig.Kind
+			newVirtualServiceConfig.MetaData.Name = newVirtualServiceConfig.ObjectMeta.Name
+			logger.Debugf("get event after asseration = %+v\n", newVirtualServiceConfig)
+			data, err := yaml.Marshal(newVirtualServiceConfig)
+			if err != nil {
+				logger.Error("Process change of virtual service: event.Value marshal error:", err)
+				return
+			}
+			r.routers, err = parseFromConfigToRouters(data, r.destinationRuleConfigBytes, r.notify)
+			if err != nil {
+				logger.Error("Process change of virtual service: parseFromConfigToRouters:", err)
+				return
+			}
+		case k8s_api.DestinationRuleEventKey:
+			logger.Debug("handling dest rule event")
+			newDRValue, ok := event.Value.(*config.DestinationRuleConfig)
+			if !ok {
+				logger.Error("event.Value assertion error")
+				return
+			}
+
+			newDRJsonValue, ok := newDRValue.ObjectMeta.Annotations["kubectl.kubernetes.io/last-applied-configuration"]
+			if !ok {
+				logger.Error("newVSValue.ObjectMeta.Annotations has no key named kubectl.kubernetes.io/last-applied-configuration")
+				return
+			}
+			newDestRuleConfig := &config.DestinationRuleConfig{}
+			if err := json.Unmarshal([]byte(newDRJsonValue), newDestRuleConfig); err != nil {
+				logger.Error("on process json data unmarshal error = ", err)
+				return
+			}
+			newDestRuleConfig.YamlAPIVersion = newDestRuleConfig.APIVersion
+			newDestRuleConfig.YamlKind = newDestRuleConfig.Kind
+			newDestRuleConfig.MetaData.Name = newDestRuleConfig.ObjectMeta.Name
+			logger.Debugf("get event after asseration = %+v\n", newDestRuleConfig)
+			data, err := yaml.Marshal(newDestRuleConfig)
+			if err != nil {
+				logger.Error("Process change of dest rule: event.Value marshal error:", err)
+				return
+			}
+			r.routers, err = parseFromConfigToRouters(r.virtualServiceConfigBytes, data, r.notify)
+			if err != nil {
+				logger.Error("Process change of dest rule: parseFromConfigToRouters:", err)
+				return
+			}
+		default:
+			logger.Error("unknow unsupported event key:", event.Key)
+		}
+	}
+
+	// todo delete router
+	//if event.ConfigType == remoting.EventTypeDel {
+	//
+	//}
+}
+
+// Name get name of ConnCheckerRouter
+func (r *RouterChain) Name() string {
+	return name
+}
+
+// Priority get Router priority level
+func (r *RouterChain) Priority() int64 {
+	return 0
+}
+
+// URL Return URL in router
+func (r *RouterChain) URL() *common.URL {
+	return nil
+}
+
+// parseFromConfigToRouters parse virtualService and destinationRule yaml file bytes to target router list
+func parseFromConfigToRouters(virtualServiceConfig, destinationRuleConfig []byte, notify chan struct{}) ([]*UniformRouter, error) {
+	var virtualServiceConfigList []*config.VirtualServiceConfig
+	destRuleConfigsMap := make(map[string]map[string]map[string]string)
+
+	vsDecoder := yaml.NewDecoder(strings.NewReader(string(virtualServiceConfig)))
+	drDecoder := yaml.NewDecoder(strings.NewReader(string(destinationRuleConfig)))
+	for {
+		virtualServiceCfg := &config.VirtualServiceConfig{}
+
+		err := vsDecoder.Decode(virtualServiceCfg)
+		if err == io.EOF {
+			break
+		}
+
+		if err != nil {
+			logger.Error("parseFromConfigTo virtual service err = ", err)
+			return nil, err
+		}
+		virtualServiceConfigList = append(virtualServiceConfigList, virtualServiceCfg)
+	}
+
+	for {
+		destRuleCfg := &config.DestinationRuleConfig{}
+		err := drDecoder.Decode(destRuleCfg)
+		if err == io.EOF {
+			break
+		}
+		if err != nil {
+			logger.Error("parseFromConfigTo destination rule err = ", err)
+			return nil, err
+		}
+		destRuleCfgMap := make(map[string]map[string]string)
+		for _, v := range destRuleCfg.Spec.SubSets {
+			destRuleCfgMap[v.Name] = v.Labels
+		}
+		destRuleConfigsMap[destRuleCfg.Spec.Host] = destRuleCfgMap
+	}
+
+	routers := make([]*UniformRouter, 0)
+
+	for _, v := range virtualServiceConfigList {
+		tempSerivceNeedsDescMap := make(map[string]map[string]string)
+		for _, host := range v.Spec.Hosts {
+			targetDestMap := destRuleConfigsMap[host]
+
+			// copy to new Map
+			mapCombine(tempSerivceNeedsDescMap, targetDestMap)
+		}
+		// change single config to one rule
+		newRule, err := newDubboRouterRule(v.Spec.Dubbo, tempSerivceNeedsDescMap)
+		if err != nil {
+			logger.Error("Parse config to uniform rule err = ", err)
+			return nil, err
+		}
+		rtr, err := NewUniformRouter(newRule, notify)
+		if err != nil {
+			logger.Error("new uniform router err = ", err)
+			return nil, err
+		}
+		routers = append(routers, rtr)
+	}
+	logger.Debug("parsed successed! with router size = ", len(routers))
+	return routers, nil
+}
+
+func mapCombine(dist map[string]map[string]string, from map[string]map[string]string) {
+	for k, v := range from {
+		dist[k] = v
+	}
+}
diff --git a/cluster/router/v3router/router_chain_test.go b/cluster/router/v3router/router_chain_test.go
new file mode 100644
index 0000000..4e05de9
--- /dev/null
+++ b/cluster/router/v3router/router_chain_test.go
@@ -0,0 +1,208 @@
+/*
+ * 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 v3router
+
+import (
+	"fmt"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/yaml"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+const (
+	mockVSConfigPath = "./test_file/virtual_service.yml"
+	mockDRConfigPath = "./test_file/dest_rule.yml"
+)
+
+func TestNewUniformRouterChain(t *testing.T) {
+	vsBytes, _ := yaml.LoadYMLConfig(mockVSConfigPath)
+	drBytes, _ := yaml.LoadYMLConfig(mockDRConfigPath)
+	rc, err := NewUniformRouterChain(vsBytes, drBytes, make(chan struct{}))
+	assert.Nil(t, err)
+	assert.NotNil(t, rc)
+}
+
+type ruleTestItemStruct struct {
+	name                             string
+	matchMethodNameExact             string
+	RouterDestHost                   string
+	RouterDestSubset                 string
+	RouterFallBackDestHost           string
+	RouterFallBackDestSubset         string
+	RouterFallBackFallBackDestHost   string
+	RouterFallBackFallBackDestSubset string
+	fallbackLevel                    int
+	RouterSize                       int
+}
+
+func TestParseConfigFromFile(t *testing.T) {
+	vsBytes, _ := yaml.LoadYMLConfig(mockVSConfigPath)
+	drBytes, _ := yaml.LoadYMLConfig(mockDRConfigPath)
+	routers, err := parseFromConfigToRouters(vsBytes, drBytes, make(chan struct{}, 1))
+	fmt.Println(routers, err)
+	assert.Equal(t, len(routers), 1)
+	assert.NotNil(t, routers[0].dubboRouter)
+	assert.Equal(t, len(routers[0].dubboRouter.uniformRules), 2)
+	for i, v := range routers[0].dubboRouter.uniformRules {
+		if i == 0 {
+			assert.Equal(t, len(v.services), 2)
+			assert.Equal(t, "com.taobao.hsf.demoService:1.0.0", v.services[0].Exact)
+			assert.Equal(t, "", v.services[0].Regex)
+			assert.Equal(t, "", v.services[0].NoEmpty)
+			assert.Equal(t, "", v.services[0].Empty)
+			assert.Equal(t, "", v.services[0].Prefix)
+
+			assert.Equal(t, "com.taobao.hsf.demoService:2.0.0", v.services[1].Exact)
+			assert.Equal(t, "", v.services[1].Regex)
+			assert.Equal(t, "", v.services[1].NoEmpty)
+			assert.Equal(t, "", v.services[1].Empty)
+			assert.Equal(t, "", v.services[1].Prefix)
+
+			assert.Equal(t, len(v.virtualServiceRules), 4)
+
+			ruleTestItemStructList := []ruleTestItemStruct{
+				{
+					name:                             "sayHello-String-method-route",
+					matchMethodNameExact:             "sayHello",
+					RouterDestHost:                   "demo",
+					RouterDestSubset:                 "v1",
+					RouterFallBackDestHost:           "demo",
+					RouterFallBackDestSubset:         "v2",
+					RouterFallBackFallBackDestHost:   "demo",
+					RouterFallBackFallBackDestSubset: "v3",
+					RouterSize:                       1,
+					fallbackLevel:                    3,
+				},
+				{
+					name:                     "sayHello-method-route",
+					matchMethodNameExact:     "s-method",
+					RouterDestHost:           "demo",
+					RouterDestSubset:         "v2",
+					RouterFallBackDestHost:   "demo",
+					RouterFallBackDestSubset: "v3",
+					RouterSize:               1,
+					fallbackLevel:            2,
+				},
+				{
+					name:                 "some-method-route",
+					matchMethodNameExact: "some-method",
+					RouterDestHost:       "demo",
+					RouterDestSubset:     "v4",
+					RouterSize:           1,
+					fallbackLevel:        1,
+				},
+				{
+					name:                             "final",
+					matchMethodNameExact:             "GetUser",
+					RouterDestHost:                   "demo",
+					RouterDestSubset:                 "v1",
+					RouterFallBackDestHost:           "demo",
+					RouterFallBackDestSubset:         "v2",
+					RouterFallBackFallBackDestHost:   "demo",
+					RouterFallBackFallBackDestSubset: "v3",
+					RouterSize:                       2,
+					fallbackLevel:                    3,
+				},
+			}
+			for i, vsRule := range v.virtualServiceRules {
+				assert.NotNil(t, v.virtualServiceRules[i].routerItem)
+				assert.Equal(t, ruleTestItemStructList[i].name, vsRule.routerItem.Name)
+				assert.Equal(t, 1, len(vsRule.routerItem.Match))
+				assert.NotNil(t, vsRule.routerItem.Match[0].Method)
+				assert.Equal(t, ruleTestItemStructList[i].matchMethodNameExact, vsRule.routerItem.Match[0].Method.NameMatch.Exact)
+				assert.Equal(t, ruleTestItemStructList[i].RouterSize, len(vsRule.routerItem.Router))
+				assert.NotNil(t, vsRule.routerItem.Router[0].Destination)
+				assert.Equal(t, ruleTestItemStructList[i].RouterDestHost, vsRule.routerItem.Router[0].Destination.Host)
+				assert.Equal(t, ruleTestItemStructList[i].RouterDestSubset, vsRule.routerItem.Router[0].Destination.Subset)
+				if vsRule.routerItem.Router[0].Destination.Fallback == nil {
+					assert.Equal(t, 1, ruleTestItemStructList[i].fallbackLevel)
+					continue
+				}
+				newRule := vsRule.routerItem.Router[0].Destination.Fallback
+				assert.NotNil(t, newRule.Destination)
+				assert.Equal(t, ruleTestItemStructList[i].RouterFallBackDestHost, newRule.Destination.Host)
+				assert.Equal(t, ruleTestItemStructList[i].RouterFallBackDestSubset, newRule.Destination.Subset)
+				if newRule.Destination.Fallback == nil {
+					assert.Equal(t, 2, ruleTestItemStructList[i].fallbackLevel)
+					continue
+				}
+
+				newRule = newRule.Destination.Fallback
+				assert.NotNil(t, newRule.Destination)
+				assert.Equal(t, ruleTestItemStructList[i].RouterFallBackFallBackDestHost, newRule.Destination.Host)
+				assert.Equal(t, ruleTestItemStructList[i].RouterFallBackFallBackDestSubset, newRule.Destination.Subset)
+				if newRule.Destination.Fallback == nil {
+					assert.Equal(t, 3, ruleTestItemStructList[i].fallbackLevel)
+				}
+			}
+
+			destMap := v.DestinationLabelListMap
+			v1Val, ok := destMap["v1"]
+			assert.True(t, ok)
+			v1SigmaVal, ok := v1Val["sigma.ali/mg"]
+			assert.True(t, ok)
+			assert.Equal(t, "v1-host", v1SigmaVal)
+			v1Generic, ok := v1Val["generic"]
+			assert.True(t, ok)
+			assert.Equal(t, "false", v1Generic)
+
+			v2Val, ok := destMap["v2"]
+			assert.True(t, ok)
+			v2Generic, ok := v2Val["generic"]
+			assert.True(t, ok)
+			assert.Equal(t, "false", v2Generic)
+
+			v3Val, ok := destMap["v3"]
+			assert.True(t, ok)
+			v3SigmaVal, ok := v3Val["sigma.ali/mg"]
+			assert.True(t, ok)
+			assert.Equal(t, "v3-host", v3SigmaVal)
+
+		}
+	}
+}
+
+func TestRouterChain_Route(t *testing.T) {
+	vsBytes, _ := yaml.LoadYMLConfig(mockVSConfigPath)
+	drBytes, _ := yaml.LoadYMLConfig(mockDRConfigPath)
+	rc, err := NewUniformRouterChain(vsBytes, drBytes, make(chan struct{}))
+	assert.Nil(t, err)
+	assert.NotNil(t, rc)
+	newGoodURL, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	newBadURL1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	newBadURL2, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0")
+	goodIvk := protocol.NewBaseInvoker(newGoodURL)
+	b1 := protocol.NewBaseInvoker(newBadURL1)
+	b2 := protocol.NewBaseInvoker(newBadURL2)
+	invokerList := make([]protocol.Invoker, 3)
+	invokerList = append(invokerList, goodIvk)
+	invokerList = append(invokerList, b1)
+	invokerList = append(invokerList, b2)
+	result := rc.Route(invokerList, newGoodURL, invocation.NewRPCInvocation("GetUser", nil, nil))
+	assert.Equal(t, 0, len(result))
+	//todo test find target invoker
+}
diff --git a/cluster/router/v3router/test_file/dest_rule.yml b/cluster/router/v3router/test_file/dest_rule.yml
new file mode 100644
index 0000000..89bd88f
--- /dev/null
+++ b/cluster/router/v3router/test_file/dest_rule.yml
@@ -0,0 +1,17 @@
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: DestinationRule
+metadata:
+  name: demo-route
+spec:
+  host: demo
+  subsets:
+    - name: v1
+      labels:
+        sigma.ali/mg: v1-host
+        generic: false
+    - name: v2
+      labels:
+        generic: false
+    - name: v3
+      labels:
+        sigma.ali/mg: v3-host
\ No newline at end of file
diff --git a/cluster/router/v3router/test_file/virtual_service.yml b/cluster/router/v3router/test_file/virtual_service.yml
new file mode 100644
index 0000000..1c6b751
--- /dev/null
+++ b/cluster/router/v3router/test_file/virtual_service.yml
@@ -0,0 +1,158 @@
+apiVersion: service.dubbo.apache.org/v1alpha1
+kind: VirtualService
+metadata:
+  name: demo-route
+spec:
+  hosts:
+    - demo  # app name
+  dubbo:
+    - services:
+        - exact: com.taobao.hsf.demoService:1.0.0
+        - exact: com.taobao.hsf.demoService:2.0.0
+      routedetail:
+        - name: sayHello-String-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "sayHello"
+          #                  argp:
+          #                    - string
+          route:
+            - destination:
+                host: demo
+                subset: v1
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v3
+
+        - name: sayHello-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "s-method"
+          route:
+            - destination:
+                host: demo
+                subset: v2
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v3
+        - name: some-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "some-method"
+          route:
+            - destination:
+                host: demo
+                subset: v4
+        - name: final
+          match:
+            - method:
+                name_match:
+                  exact: "GetUser"
+
+          route:
+            - destination:
+                host: demo
+                subset: v1
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v3
+            - destination:
+                host: demo
+                subset: v3
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v1
+    - services:
+        - exact: com.taobao.hsf.demoService:1.0.0
+        - exact: org.apache.dubbo.UserProvider
+      routedetail:
+        - name: sayHello-String-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "sayHello"
+          #                  argp:
+          #                    - string
+          route:
+            - destination:
+                host: demo
+                subset: v1
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v3
+
+        - name: sayHello-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "s-method"
+          route:
+            - destination:
+                host: demo
+                subset: v2
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v3
+        - name: some-method-route
+          match:
+            - method:
+                name_match:
+                  exact: "some-method"
+          route:
+            - destination:
+                host: demo
+                subset: v4
+        - name: final
+          match:
+            - method:
+                name_match:
+                  exact: "GetUser"
+
+          route:
+            - destination:
+                host: demo
+                subset: v1
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v3
+            - destination:
+                host: demo
+                subset: v3
+                fallback:
+                  destination:
+                    host: demo
+                    subset: v2
+                    fallback:
+                      destination:
+                        host: demo
+                        subset: v1
\ No newline at end of file
diff --git a/cluster/router/v3router/uniform_route.go b/cluster/router/v3router/uniform_route.go
new file mode 100644
index 0000000..c11c296
--- /dev/null
+++ b/cluster/router/v3router/uniform_route.go
@@ -0,0 +1,67 @@
+/*
+ * 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 v3router
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/config_center"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+const (
+	name = "uniform-router"
+)
+
+// UniformRouter have
+type UniformRouter struct {
+	dubboRouter *DubboRouterRule
+	notify      chan struct{}
+}
+
+// NewUniformRouter construct an NewConnCheckRouter via url
+func NewUniformRouter(dubboRouter *DubboRouterRule, notify chan struct{}) (*UniformRouter, error) {
+	r := &UniformRouter{
+		dubboRouter: dubboRouter,
+		notify:      notify,
+	}
+	return r, nil
+}
+
+// Route gets a list of routed invoker
+func (r *UniformRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	return r.dubboRouter.route(invokers, url, invocation)
+}
+
+// Process there is no process needs for uniform Router, as it upper struct RouterChain has done it
+func (r *UniformRouter) Process(event *config_center.ConfigChangeEvent) {
+}
+
+// Name get name of ConnCheckerRouter
+func (r *UniformRouter) Name() string {
+	return name
+}
+
+// Priority get Router priority level
+func (r *UniformRouter) Priority() int64 {
+	return 0
+}
+
+// URL Return URL in router
+func (r *UniformRouter) URL() *common.URL {
+	return nil
+}
diff --git a/cluster/router/v3router/uniform_rule.go b/cluster/router/v3router/uniform_rule.go
new file mode 100644
index 0000000..f7e8148
--- /dev/null
+++ b/cluster/router/v3router/uniform_rule.go
@@ -0,0 +1,257 @@
+/*
+ * 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 v3router
+
+import (
+	"math/rand"
+	"time"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router/v3router/judger"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/logger"
+	"github.com/apache/dubbo-go/config"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// VirtualServiceRule is item of virtual service, it aims at judge if invocation context match it's condition, and
+// if match, get result destination key, which should be defined in DestinationRule yaml file
+type VirtualServiceRule struct {
+	// routerItem store match router list and destination list of this router
+	routerItem *config.DubboServiceRouterItem
+
+	// uniformRule is the upper struct ptr
+	uniformRule *UniformRule
+}
+
+// match read from vsr's Match config
+// it judges if this invocation matches the router rule request defined in config one by one
+func (vsr *VirtualServiceRule) match(url *common.URL, invocation protocol.Invocation) bool {
+	for _, v := range vsr.routerItem.Match {
+		// method match judge
+		if v.Method != nil {
+			methodMatchJudger := judger.NewMethodMatchJudger(v.Method)
+			if !methodMatchJudger.Judge(invocation) {
+				return false
+			}
+		}
+
+		// source label match judge
+		if !judger.JudgeUrlLabel(url, v.SourceLabels) {
+			return false
+		}
+
+		// atta match judge
+		if v.Attachment != nil {
+			attachmentMatchJudger := judger.NewAttachmentMatchJudger(v.Attachment)
+			if attachmentMatchJudger.Judge(invocation) {
+				return false
+			}
+		}
+		// threshold match judge
+		// todo
+
+		// reserve match judge
+		// todo
+	}
+	return true
+}
+
+// tryGetSubsetFromRouterOfOneDestination is a recursion function
+// try from destination 's header to final fallback destination, when success, it return result destination, else return error
+func (vsr *VirtualServiceRule) tryGetSubsetFromRouterOfOneDestination(desc *config.DubboDestination, invokers []protocol.Invoker) ([]protocol.Invoker, int, error) {
+	subSet := desc.Destination.Subset
+	labels, ok := vsr.uniformRule.DestinationLabelListMap[subSet]
+	resultInvokers := make([]protocol.Invoker, 0)
+	if ok {
+		for _, v := range invokers {
+			if judger.JudgeUrlLabel(v.GetURL(), labels) {
+				resultInvokers = append(resultInvokers, v)
+			}
+		}
+		if len(resultInvokers) != 0 {
+			return resultInvokers, desc.Destination.Weight, nil
+		}
+	}
+
+	if desc.Destination.Fallback != nil {
+		return vsr.tryGetSubsetFromRouterOfOneDestination(desc.Destination.Fallback, invokers)
+	}
+	return nil, 0, perrors.New("No invoker matches and no fallback destination to choose!")
+}
+
+//weightInvokersPair stores weight and invoker list.
+type weightInvokersPair struct {
+	weight      int
+	invokerList []protocol.Invoker
+}
+
+type weightInvokerPairResults struct {
+	pairs []weightInvokersPair
+}
+
+func (w *weightInvokerPairResults) getTargetInvokers() []protocol.Invoker {
+	if len(w.pairs) == 0 {
+		return []protocol.Invoker{}
+	}
+
+	if len(w.pairs) == 1 {
+		return w.pairs[0].invokerList
+	}
+	rand.Seed(time.Now().UnixNano())
+	target := rand.Intn(100)
+	// noweight means all weigh is zero, random choose one invoker list
+	noWeight := true
+	// check if empty
+	for _, v := range w.pairs {
+		if v.weight != 0 {
+			noWeight = false // user defined weight
+			break
+		}
+	}
+	if noWeight {
+		// random choose one list
+		weitUnit := 100/len(w.pairs) + 1
+		return w.pairs[target/weitUnit].invokerList
+	} else {
+		total := 0
+		for _, v := range w.pairs {
+			total += v.weight
+			if total > target {
+				return v.invokerList
+			}
+		}
+	}
+	// invalid weight set: total is smaller than 100, choose first
+	return w.pairs[0].invokerList
+}
+
+func (vsr *VirtualServiceRule) getRuleTargetInvokers(invokers []protocol.Invoker) ([]protocol.Invoker, error) {
+	// weightInvokerPairResult is the collection routerDesc of all destination fields,
+	weightInvokerPairResult := weightInvokerPairResults{}
+	for _, v := range vsr.routerItem.Router {
+		// v is one destination 's header e.g.
+		/*
+			   route:
+				 - destination:      # v is here
+					 host: demo
+					 subset: v1
+				   fallback:
+					 destination:
+					   host: demo
+					   subset: v2
+					 fallback:
+					   destination:
+						 host: demo
+						 subset: v3
+				 - destination:
+					 host: demo
+					 subset: v4
+				   fallback:
+					 destination:
+					   host: demo
+					   subset: v5
+					 fallback:
+					   destination:
+						 host: demo
+						 subset: v6
+		*/
+		invokerListOfOneDest, weight, err := vsr.tryGetSubsetFromRouterOfOneDestination(v, invokers)
+		if err != nil {
+			return nil, err
+		}
+		// combination of all destination field e.g.
+		/*
+			 - destination:
+			   host: demo
+			   subset: na61
+			- destination:
+			   host: demo
+			   subset: na610
+		*/
+		weightInvokerPairResult.pairs = append(weightInvokerPairResult.pairs, weightInvokersPair{
+			weight:      weight,
+			invokerList: invokerListOfOneDest,
+		})
+	}
+
+	return weightInvokerPairResult.getTargetInvokers(), nil
+}
+
+// UniformRule
+type UniformRule struct {
+	services                []*config.StringMatch
+	virtualServiceRules     []VirtualServiceRule
+	DestinationLabelListMap map[string]map[string]string
+}
+
+// NewDefaultConnChecker constructs a new DefaultConnChecker based on the url
+func newUniformRule(dubboRoute *config.DubboRoute, destinationMap map[string]map[string]string) (*UniformRule, error) {
+	matchItems := dubboRoute.RouterDetail
+	virtualServiceRules := make([]VirtualServiceRule, 0)
+	newUniformRule := &UniformRule{
+		DestinationLabelListMap: destinationMap,
+		services:                dubboRoute.Services,
+	}
+	for _, v := range matchItems {
+		virtualServiceRules = append(virtualServiceRules, VirtualServiceRule{
+			routerItem:  v,
+			uniformRule: newUniformRule,
+		})
+	}
+	newUniformRule.virtualServiceRules = virtualServiceRules
+	return newUniformRule, nil
+}
+
+func (u *UniformRule) route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	// service rule + destination -> filter
+	resultInvokers := make([]protocol.Invoker, 0)
+	var err error
+	matchService := false
+	for _, v := range u.services {
+		// check if match service field
+		if judger.NewStringMatchJudger(v).Judge(url.ServiceKey()) {
+			matchService = true
+			break
+		}
+	}
+	if !matchService {
+		// if not match, jump this rule
+		return resultInvokers
+	}
+	// match service field, route Details level(service level) match
+	// then, check all sub rule, if match, get destination rule target invokers, else do fail back logic
+	for _, rule := range u.virtualServiceRules {
+		if rule.match(url, invocation) {
+			// match this rule, do get target logic
+			resultInvokers, err = rule.getRuleTargetInvokers(invokers)
+			if err != nil {
+				logger.Error("getRuleTargetInvokers from rule err = ", err)
+				return nil
+			}
+			return resultInvokers
+		}
+	}
+	logger.Errorf("no match rule for invokers %+v", invokers)
+	return resultInvokers
+}
diff --git a/config/uniform_router_config.go b/config/uniform_router_config.go
new file mode 100644
index 0000000..e18c07c
--- /dev/null
+++ b/config/uniform_router_config.go
@@ -0,0 +1,231 @@
+/*
+ * 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 config
+
+import (
+	"github.com/ghodss/yaml"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+)
+
+// nolint
+type MetaDataStruct struct {
+	Name string `yaml:"name"`
+}
+
+// VirtualService Config Definition
+type VirtualServiceConfig struct {
+	YamlAPIVersion    string `yaml:"apiVersion"`
+	YamlKind          string `yaml:"kind"`
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+	MetaData          MetaDataStruct          `yaml:"metadata"`
+	Spec              UniformRouterConfigSpec `yaml:"spec" json:"spec"`
+}
+
+// nolint
+type UniformRouterConfigSpec struct {
+	Hosts []string      `yaml:"hosts" json:"hosts"`
+	Dubbo []*DubboRoute `yaml:"dubbo" json:"dubbo"`
+}
+
+// nolint
+type DubboRoute struct {
+	Services     []*StringMatch            `yaml:"services" json:"service"`
+	RouterDetail []*DubboServiceRouterItem `yaml:"routedetail" json:"routedetail"`
+}
+
+// nolint
+type DubboServiceRouterItem struct {
+	Name   string               `yaml:"name" json:"name"`
+	Match  []*DubboMatchRequest `yaml:"match" json:"match"`
+	Router []*DubboDestination  `yaml:"route" json:"route"`
+	// todo mirror/retries/timeout
+}
+
+// nolint
+type DubboMatchRequest struct {
+	Name         string                  `yaml:"name" json:"name"`
+	Method       *DubboMethodMatch       `yaml:"method" json:"method"`
+	SourceLabels map[string]string       `yaml:"sourceLabels" json:"sourceLabels"`
+	Attachment   *DubboAttachmentMatch   `yaml:"attachments" json:"attachments"`
+	Header       map[string]*StringMatch `yaml:"headers" json:"headers"`
+	Threshold    *DoubleMatch            `yaml:"threshold" json:"threshold"`
+}
+
+// nolint
+type DoubleMatch struct {
+	Exact float64           `yaml:"exact" json:"exact"`
+	Range *DoubleRangeMatch `yaml:"range" json:"range"`
+	Mode  float64           `yaml:"mode" json:"mode"`
+}
+
+// nolint
+type DoubleRangeMatch struct {
+	Start float64 `yaml:"start" json:"start"`
+	End   float64 `yaml:"end" json:"end"`
+}
+
+// nolint
+type DubboAttachmentMatch struct {
+	EagleeyeContext map[string]*StringMatch `yaml:"eagleeyecontext" json:"eagleeyecontext"`
+	DubboContext    map[string]*StringMatch `yaml:"dubbocontext" json:"dubbocontext"`
+}
+
+// nolint
+type DubboMethodMatch struct {
+	NameMatch *StringMatch            `yaml:"name_match" json:"name_match"`
+	Argc      int                     `yaml:"argc" json:"argc"`
+	Args      []*DubboMethodArg       `yaml:"args" json:"args"`
+	Argp      []*StringMatch          `yaml:"argp" json:"argp"`
+	Headers   map[string]*StringMatch `yaml:"headers" json:"headers"`
+}
+
+// nolint
+type DubboMethodArg struct {
+	Index     uint32           `yaml:"index" json:"index"`
+	Type      string           `yaml:"type" json:"type"`
+	StrValue  *ListStringMatch `yaml:"str_value" json:"str_value"`
+	NumValue  *ListDoubleMatch `yaml:"num_value" json:"num_value"`
+	BoolValue *BoolMatch       `yaml:"bool_value" json:"bool_value"`
+	//todo reserve field
+}
+
+// nolint
+type ListStringMatch struct {
+	Oneof []*StringMatch `yaml:"oneof" json:"oneof"`
+}
+
+// nolint
+type ListDoubleMatch struct {
+	Oneof []*DoubleMatch `yaml:"oneof" json:"oneof"`
+}
+
+// nolint
+type BoolMatch struct {
+	Exact bool `yaml:"exact" json:"exact"`
+}
+
+// nolint
+type StringMatch struct {
+	Exact   string `yaml:"exact" json:"exact"`
+	Prefix  string `yaml:"prefix" json:"prefix"`
+	Regex   string `yaml:"regex" json:"regex"`
+	NoEmpty string `yaml:"noempty" json:"noempty"`
+	Empty   string `yaml:"empty" json:"empty"`
+}
+
+// nolint
+type DubboDestination struct {
+	Destination RouterDest `yaml:"destination" json:"destination"`
+	//Subset      string            `yaml:"subset"`
+}
+
+// nolint
+type RouterDest struct {
+	Host     string            `yaml:"host" json:"host"`
+	Subset   string            `yaml:"subset" json:"subset"`
+	Weight   int               `yaml:"weight" json:"weight"`
+	Fallback *DubboDestination `yaml:"fallback" json:"fallback"`
+	// todo port
+}
+
+// DestinationRule Definition
+type DestinationRuleConfig struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+	YamlAPIVersion    string              `yaml:"apiVersion" `
+	YamlKind          string              `yaml:"kind" `
+	MetaData          MetaDataStruct      `yaml:"metadata"`
+	Spec              DestinationRuleSpec `yaml:"spec" json:"spec"`
+}
+
+// nolint
+func (drc *DestinationRuleConfig) DeepCopyObject() runtime.Object {
+	data, _ := yaml.Marshal(drc)
+	out := &DestinationRuleConfig{}
+	yaml.Unmarshal(data, out)
+	return out
+}
+
+// nolint
+type DestinationRuleSpec struct {
+	Host    string                  `yaml:"host" json:"host"`
+	SubSets []DestinationRuleSubSet `yaml:"subsets" json:"subsets"`
+}
+
+// nolint
+type DestinationRuleSubSet struct {
+	Name   string            `yaml:"name" json:"name"`
+	Labels map[string]string `yaml:"labels" json:"labels"`
+}
+
+// nolint
+func (urc *VirtualServiceConfig) DeepCopyObject() runtime.Object {
+	data, _ := yaml.Marshal(urc)
+	out := &VirtualServiceConfig{}
+	yaml.Unmarshal(data, out)
+	return out
+}
+
+// nolint
+type DestinationRuleSpecList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []DestinationRuleSpec `json:"items"`
+}
+
+// nolint
+type VirtualServiceConfigList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []VirtualServiceConfig `json:"items"`
+}
+
+// nolint
+func (drc *VirtualServiceConfigList) DeepCopyObject() runtime.Object {
+	out := &VirtualServiceConfigList{
+		TypeMeta: drc.TypeMeta,
+		ListMeta: drc.ListMeta,
+	}
+	for _, v := range drc.Items {
+		spec := v.DeepCopyObject().(*VirtualServiceConfig)
+		out.Items = append(out.Items, *spec)
+	}
+	return out
+}
+
+// nolint
+type DestinationRuleConfigList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []DestinationRuleConfig `json:"items"`
+}
+
+// nolint
+func (drc *DestinationRuleConfigList) DeepCopyObject() runtime.Object {
+	out := &DestinationRuleConfigList{
+		TypeMeta: drc.TypeMeta,
+		ListMeta: drc.ListMeta,
+	}
+	for _, v := range drc.Items {
+		spec := v.DeepCopyObject().(*DestinationRuleConfig)
+		out.Items = append(out.Items, *spec)
+	}
+	return out
+}
diff --git a/go.mod b/go.mod
index b505156..ebe4b7f 100644
--- a/go.mod
+++ b/go.mod
@@ -16,11 +16,12 @@ require (
 	github.com/docker/go-connections v0.4.0 // indirect
 	github.com/dubbogo/go-zookeeper v1.0.3
 	github.com/dubbogo/gost v1.11.8
-	github.com/dubbogo/triple v0.1.3
+	github.com/dubbogo/triple v1.0.0
 	github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect
 	github.com/emicklei/go-restful/v3 v3.4.0
 	github.com/frankban/quicktest v1.4.1 // indirect
 	github.com/fsnotify/fsnotify v1.4.9
+	github.com/ghodss/yaml v1.0.0
 	github.com/go-co-op/gocron v0.1.1
 	github.com/go-resty/resty/v2 v2.3.0
 	github.com/golang/mock v1.4.4
diff --git a/go.sum b/go.sum
index e280dc9..f672271 100644
--- a/go.sum
+++ b/go.sum
@@ -150,7 +150,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
 github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -188,8 +187,8 @@ github.com/dubbogo/gost v1.11.8/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG
 github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU=
 github.com/dubbogo/net v0.0.2-0.20210326124702-e6a866993192 h1:CBEicrrVwR6u8ty+kL68ItxXVk1jaVYThrsx5ARhxUc=
 github.com/dubbogo/net v0.0.2-0.20210326124702-e6a866993192/go.mod h1:B6/ka3g8VzcyrmdCH4VkHP1K0aHeI37FmclS+TCwIBU=
-github.com/dubbogo/triple v0.1.3 h1:Tn+oK49JhSN1iOCRxT1DuKZJOPeySV0zc6ap+g3xjts=
-github.com/dubbogo/triple v0.1.3/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
+github.com/dubbogo/triple v1.0.0 h1:mbFU078PmyjO8OywtrNsTZW0RSYIImVGyXE4wLO3Vm0=
+github.com/dubbogo/triple v1.0.0/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -226,6 +225,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
 github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
@@ -820,7 +820,6 @@ github.com/zouyx/agollo/v3 v3.4.5 h1:7YCxzY9ZYaH9TuVUBvmI6Tk0mwMggikah+cfbYogcHQ
 github.com/zouyx/agollo/v3 v3.4.5/go.mod h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
 go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698 h1:jWtjCJX1qxhHISBMLRztWwR+EXkI7MJAF2HjHAE/x/I=
diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum
index 75949aa..114d619 100644
--- a/test/integrate/dubbo/go-client/go.sum
+++ b/test/integrate/dubbo/go-client/go.sum
@@ -153,12 +153,14 @@ github.com/dubbogo/gost v1.11.3/go.mod h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7F
 github.com/dubbogo/gost v1.11.5/go.mod h1:Q//0o51ooLfwpWzZMftJLvIitNEZXH3rRKkxdu37xJg=
 github.com/dubbogo/gost v1.11.6/go.mod h1:Q//0o51ooLfwpWzZMftJLvIitNEZXH3rRKkxdu37xJg=
 github.com/dubbogo/gost v1.11.7/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
+github.com/dubbogo/gost v1.11.8/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
 github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU=
 github.com/dubbogo/net v0.0.2-0.20210326124702-e6a866993192/go.mod h1:B6/ka3g8VzcyrmdCH4VkHP1K0aHeI37FmclS+TCwIBU=
 github.com/dubbogo/triple v0.0.0-20210403061850-372f2dc47e02/go.mod h1:ArB/FJCvy6DQXs8tW6LmNRtLaZuncGmDHqD/1JOAwsU=
 github.com/dubbogo/triple v0.1.0/go.mod h1:ArB/FJCvy6DQXs8tW6LmNRtLaZuncGmDHqD/1JOAwsU=
 github.com/dubbogo/triple v0.1.2/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
 github.com/dubbogo/triple v0.1.3/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
+github.com/dubbogo/triple v1.0.0/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
 github.com/dubbogo/v3router v0.0.0-20210403094230-574be83d8d6d/go.mod h1:prAeY9nOJfo09GBDh5DMV4khJhQOhy5JnLdMCzVk18c=
 github.com/dubbogo/v3router v0.1.0/go.mod h1:lJ2qS8FLme8RXE8hq4kKdh/6aRi2sRq5MilrITnNxcE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
diff --git a/test/integrate/dubbo/go-server/go.sum b/test/integrate/dubbo/go-server/go.sum
index 75949aa..114d619 100644
--- a/test/integrate/dubbo/go-server/go.sum
+++ b/test/integrate/dubbo/go-server/go.sum
@@ -153,12 +153,14 @@ github.com/dubbogo/gost v1.11.3/go.mod h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7F
 github.com/dubbogo/gost v1.11.5/go.mod h1:Q//0o51ooLfwpWzZMftJLvIitNEZXH3rRKkxdu37xJg=
 github.com/dubbogo/gost v1.11.6/go.mod h1:Q//0o51ooLfwpWzZMftJLvIitNEZXH3rRKkxdu37xJg=
 github.com/dubbogo/gost v1.11.7/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
+github.com/dubbogo/gost v1.11.8/go.mod h1:2nB8jSrxVPwW5DBsRu3FZQH1+Ual3wnRHwFqjG9+4PY=
 github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU=
 github.com/dubbogo/net v0.0.2-0.20210326124702-e6a866993192/go.mod h1:B6/ka3g8VzcyrmdCH4VkHP1K0aHeI37FmclS+TCwIBU=
 github.com/dubbogo/triple v0.0.0-20210403061850-372f2dc47e02/go.mod h1:ArB/FJCvy6DQXs8tW6LmNRtLaZuncGmDHqD/1JOAwsU=
 github.com/dubbogo/triple v0.1.0/go.mod h1:ArB/FJCvy6DQXs8tW6LmNRtLaZuncGmDHqD/1JOAwsU=
 github.com/dubbogo/triple v0.1.2/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
 github.com/dubbogo/triple v0.1.3/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
+github.com/dubbogo/triple v1.0.0/go.mod h1:duJFWIYOE+Ajxxvk+AOBpEBePFNnviJ52fveZ3vYA04=
 github.com/dubbogo/v3router v0.0.0-20210403094230-574be83d8d6d/go.mod h1:prAeY9nOJfo09GBDh5DMV4khJhQOhy5JnLdMCzVk18c=
 github.com/dubbogo/v3router v0.1.0/go.mod h1:lJ2qS8FLme8RXE8hq4kKdh/6aRi2sRq5MilrITnNxcE=
 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=