From 3ce09055ceb2619751128fec376e7a354b4407de Mon Sep 17 00:00:00 2001
From: pantianying <601666418@qq.com>
Date: Thu, 12 Mar 2020 11:02:59 +0800
Subject: [PATCH] parent bde7db3b33067ee4115ea3f8666ec1169226a5de author
 pantianying <601666418@qq.com> 1583982179 +0800 committer pantianying
 <601666418@qq.com> 1584417587 +0800

parent bde7db3b33067ee4115ea3f8666ec1169226a5de
author pantianying <601666418@qq.com> 1583982179 +0800
committer pantianying <601666418@qq.com> 1584417185 +0800

add code for tag router

fix bug

fix

fix bug

fix
---
 cluster/router/router.go                      |   2 +-
 cluster/router/tag/factory.go                 |  41 ++++++
 cluster/router/tag/factory_test.go            |  39 ++++++
 cluster/router/tag/file.go                    |  83 +++++++++++
 cluster/router/tag/router_rule.go             |  41 ++++++
 cluster/router/tag/tag_router.go              |  89 ++++++++++++
 cluster/router/tag/tag_router_test.go         | 132 ++++++++++++++++++
 common/constant/key.go                        |   8 +-
 common/extension/router_factory.go            |   6 +-
 config/reference_config.go                    |   5 +-
 ...tion_router_config.go => router_config.go} |   5 +-
 ...r_config_test.go => router_config_test.go} |   0
 config/service_config.go                      |   5 +-
 registry/directory/directory.go               |   5 +-
 14 files changed, 449 insertions(+), 12 deletions(-)
 create mode 100644 cluster/router/tag/factory.go
 create mode 100644 cluster/router/tag/factory_test.go
 create mode 100644 cluster/router/tag/file.go
 create mode 100644 cluster/router/tag/router_rule.go
 create mode 100644 cluster/router/tag/tag_router.go
 create mode 100644 cluster/router/tag/tag_router_test.go
 rename config/{condition_router_config.go => router_config.go} (89%)
 rename config/{condition_router_config_test.go => router_config_test.go} (100%)

diff --git a/cluster/router/router.go b/cluster/router/router.go
index a28002a09..9ee115443 100644
--- a/cluster/router/router.go
+++ b/cluster/router/router.go
@@ -31,7 +31,7 @@ type RouterFactory interface {
 }
 
 // RouterFactory Router create factory use for parse config file
-type FIleRouterFactory interface {
+type FileRouterFactory interface {
 	// NewFileRouters Create file router with config file
 	NewFileRouter([]byte) (Router, error)
 }
diff --git a/cluster/router/tag/factory.go b/cluster/router/tag/factory.go
new file mode 100644
index 000000000..84b8d6aa3
--- /dev/null
+++ b/cluster/router/tag/factory.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 tag
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/common/extension"
+)
+
+func init() {
+	extension.SetRouterFactory(constant.TagRouterName, newtagRouterFactory)
+}
+
+type tagRouterFactory struct{}
+
+func newtagRouterFactory() router.RouterFactory {
+	return &tagRouterFactory{}
+}
+func (c *tagRouterFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewTagRouter(url)
+}
+func (c *tagRouterFactory) NewFileRouter(content []byte) (router.Router, error) {
+	return NewFileTagRouter(content)
+}
diff --git a/cluster/router/tag/factory_test.go b/cluster/router/tag/factory_test.go
new file mode 100644
index 000000000..ecc88066f
--- /dev/null
+++ b/cluster/router/tag/factory_test.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 tag
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func TestTagRouterFactory_NewRouter(t *testing.T) {
+	u1, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true")
+	assert.Nil(t, err)
+	factory := newtagRouterFactory()
+	tagRouter, e := factory.NewRouter(&u1)
+	assert.Nil(t, e)
+	assert.NotNil(t, tagRouter)
+}
diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go
new file mode 100644
index 000000000..b9d67284a
--- /dev/null
+++ b/cluster/router/tag/file.go
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"net/url"
+	"strconv"
+	"sync"
+)
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+// FileTagRouter Use for parse config file of Tag router
+type FileTagRouter struct {
+	parseOnce  sync.Once
+	router     *tagRouter
+	routerRule *RouterRule
+	url        *common.URL
+	force      bool
+	priority   int64
+}
+
+// NewFileTagRouter Create file tag router instance with content ( from config file)
+func NewFileTagRouter(content []byte) (*FileTagRouter, error) {
+	fileRouter := &FileTagRouter{}
+	rule, err := Parse(string(content))
+	if err != nil {
+		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
+	}
+	fileRouter.routerRule = rule
+	url := fileRouter.URL()
+	fileRouter.router, err = NewTagRouter(&url)
+	return fileRouter, err
+}
+
+// URL Return URL in file tag router n
+func (f *FileTagRouter) URL() common.URL {
+	f.parseOnce.Do(func() {
+		routerRule := f.routerRule
+		f.url = common.NewURLWithOptions(
+			common.WithProtocol(constant.TAG_ROUTE_PROTOCOL),
+			common.WithParams(url.Values{}),
+			common.WithParamsValue(constant.ForceUseTag, strconv.FormatBool(routerRule.Force)),
+			common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)),
+			common.WithParamsValue(constant.ROUTER_KEY, constant.TAG_ROUTE_PROTOCOL))
+	})
+	return *f.url
+}
+
+// Priority Return Priority in listenable router
+func (f *FileTagRouter) Priority() int64 {
+	return f.priority
+}
+
+func (f *FileTagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if len(invokers) == 0 {
+		return invokers
+	}
+	return f.Route(invokers, url, invocation)
+}
diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go
new file mode 100644
index 000000000..3e910a9a0
--- /dev/null
+++ b/cluster/router/tag/router_rule.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 tag
+
+import (
+	"gopkg.in/yaml.v2"
+)
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+)
+
+// RouterRule RouterRule config read from config file or config center
+type RouterRule struct {
+	router.BaseRouterRule `yaml:",inline""`
+}
+
+func Parse(rawRule string) (*RouterRule, error) {
+	r := &RouterRule{}
+	err := yaml.Unmarshal([]byte(rawRule), r)
+	if err != nil {
+		return r, err
+	}
+	r.RawRule = rawRule
+	return r, nil
+}
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
new file mode 100644
index 000000000..42ffb4c93
--- /dev/null
+++ b/cluster/router/tag/tag_router.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 tag
+
+import (
+	perrors "github.com/pkg/errors"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/constant"
+	"github.com/apache/dubbo-go/protocol"
+)
+
+type tagRouter struct {
+	url      *common.URL
+	enabled  bool
+	priority int64
+}
+
+func NewTagRouter(url *common.URL) (*tagRouter, error) {
+	if url == nil {
+		return nil, perrors.Errorf("Illegal route URL!")
+	}
+	return &tagRouter{
+		url:      url,
+		enabled:  url.GetParamBool(constant.RouterEnabled, true),
+		priority: url.GetParamInt(constant.RouterPriority, 0),
+	}, nil
+}
+
+func (c *tagRouter) isEnabled() bool {
+	return c.enabled
+}
+
+func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if !c.isEnabled() {
+		return invokers
+	}
+	if len(invokers) == 0 {
+		return invokers
+	}
+	return filterUsingStaticTag(invokers, url, invocation)
+}
+
+func (c *tagRouter) URL() common.URL {
+	return *c.url
+}
+func (c *tagRouter) Priority() int64 {
+	return c.priority
+}
+
+func filterUsingStaticTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+	if tag, ok := invocation.Attachments()[constant.Tagkey]; ok {
+		result := make([]protocol.Invoker, 0)
+		for _, v := range invokers {
+			if v.GetUrl().GetParam(constant.Tagkey, "") == tag {
+				result = append(result, v)
+			}
+		}
+		if len(result) == 0 && !isForceUseTag(url, invocation) {
+			return invokers
+		}
+		return result
+	} else {
+		return invokers
+	}
+}
+func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
+	if invocation.AttachmentsByKey(constant.ForceUseTag, url.GetParam(constant.ForceUseTag, "false")) == "true" {
+		return true
+	}
+	return false
+}
diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go
new file mode 100644
index 000000000..bf2324a12
--- /dev/null
+++ b/cluster/router/tag/tag_router_test.go
@@ -0,0 +1,132 @@
+/*
+ * 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 tag
+
+import (
+	"context"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+type MockInvoker struct {
+	url          common.URL
+	available    bool
+	destroyed    bool
+	successCount int
+}
+
+func NewMockInvoker(url common.URL) *MockInvoker {
+	return &MockInvoker{
+		url:          url,
+		available:    true,
+		destroyed:    false,
+		successCount: 0,
+	}
+}
+
+func (bi *MockInvoker) GetUrl() common.URL {
+	return bi.url
+}
+
+func (bi *MockInvoker) IsAvailable() bool {
+	return bi.available
+}
+
+func (bi *MockInvoker) IsDestroyed() bool {
+	return bi.destroyed
+}
+
+func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result {
+	bi.successCount++
+
+	result := &protocol.RPCResult{Err: nil}
+	return result
+}
+
+func (bi *MockInvoker) Destroy() {
+	bi.destroyed = true
+	bi.available = false
+}
+
+func TestTagRouter_Priority(t *testing.T) {
+	u1, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true")
+	assert.Nil(t, err)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+	p := tagRouter.Priority()
+	assert.Equal(t, int64(0), p)
+}
+func TestTagRouter_Route_force(t *testing.T) {
+	u1, e1 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true")
+	assert.Nil(t, e1)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+
+	u2, e2 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou")
+	u3, e3 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai")
+	u4, e4 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing")
+	assert.Nil(t, e2)
+	assert.Nil(t, e3)
+	assert.Nil(t, e4)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	var invokers []protocol.Invoker
+	invokers = append(invokers, inv2, inv3, inv4)
+	inv := &invocation.RPCInvocation{}
+	inv.SetAttachments("dubbo.tag", "hangzhou")
+	invRst1 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 1, len(invRst1))
+	assert.Equal(t, "hangzhou", invRst1[0].GetUrl().GetParam("dubbo.tag", ""))
+
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	invRst2 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 0, len(invRst2))
+}
+
+func TestTagRouter_Route_noForce(t *testing.T) {
+	u1, e1 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true")
+	assert.Nil(t, e1)
+	tagRouter, e := NewTagRouter(&u1)
+	assert.Nil(t, e)
+
+	u2, e2 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou")
+	u3, e3 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai")
+	u4, e4 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing")
+	assert.Nil(t, e2)
+	assert.Nil(t, e3)
+	assert.Nil(t, e4)
+	inv2 := NewMockInvoker(u2)
+	inv3 := NewMockInvoker(u3)
+	inv4 := NewMockInvoker(u4)
+	var invokers []protocol.Invoker
+	invokers = append(invokers, inv2, inv3, inv4)
+	inv := &invocation.RPCInvocation{}
+	inv.SetAttachments("dubbo.tag", "hangzhou")
+	invRst := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 1, len(invRst))
+	assert.Equal(t, "hangzhou", invRst[0].GetUrl().GetParam("dubbo.tag", ""))
+
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	inv.SetAttachments("dubbo.force.tag", "true")
+	invRst1 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 0, len(invRst1))
+}
diff --git a/common/constant/key.go b/common/constant/key.go
index 07335bed5..62908e448 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -105,6 +105,7 @@ const (
 	ROUTERS_CATEGORY         = "routers"
 	ROUTE_PROTOCOL           = "route"
 	CONDITION_ROUTE_PROTOCOL = "condition"
+	TAG_ROUTE_PROTOCOL       = "tag"
 	PROVIDERS_CATEGORY       = "providers"
 	ROUTER_KEY               = "router"
 )
@@ -161,7 +162,8 @@ const (
 	ListenableRouterName = "listenable"
 	// HealthCheckRouterName Specify the name of HealthCheckRouter
 	HealthCheckRouterName = "health_check"
-
+	// TagRouterName Specify the name of TagRouter
+	TagRouterName = "tag"
 	// ConditionRouterRuleSuffix Specify condition router suffix
 	ConditionRouterRuleSuffix = ".condition-router"
 
@@ -171,6 +173,10 @@ const (
 	RouterEnabled = "enabled"
 	// Priority Priority key in router module
 	RouterPriority = "priority"
+
+	// ForceUseTag is the tag in attachment
+	ForceUseTag = "dubbo.force.tag"
+	Tagkey      = "dubbo.tag"
 )
 
 const (
diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go
index 70d71dfa8..133922861 100644
--- a/common/extension/router_factory.go
+++ b/common/extension/router_factory.go
@@ -28,7 +28,7 @@ import (
 var (
 	routers               = make(map[string]func() router.RouterFactory)
 	fileRouterFactoryOnce sync.Once
-	fileRouterFactories   = make(map[string]router.FIleRouterFactory)
+	fileRouterFactories   = make(map[string]router.FileRouterFactory)
 )
 
 // SetRouterFactory Set create router factory function by name
@@ -50,7 +50,7 @@ func GetRouterFactories() map[string]func() router.RouterFactory {
 }
 
 // GetFileRouterFactories Get all create file router factory instance
-func GetFileRouterFactories() map[string]router.FIleRouterFactory {
+func GetFileRouterFactories() map[string]router.FileRouterFactory {
 	l := len(routers)
 	if l == 0 {
 		return nil
@@ -58,7 +58,7 @@ func GetFileRouterFactories() map[string]router.FIleRouterFactory {
 	fileRouterFactoryOnce.Do(func() {
 		for k := range routers {
 			factory := GetRouterFactory(k)
-			if fr, ok := factory.(router.FIleRouterFactory); ok {
+			if fr, ok := factory.(router.FileRouterFactory); ok {
 				fileRouterFactories[k] = fr
 			}
 		}
diff --git a/config/reference_config.go b/config/reference_config.go
index 7ce001319..cf4de6834 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -63,6 +63,7 @@ type ReferenceConfig struct {
 	Generic        bool   `yaml:"generic"  json:"generic,omitempty" property:"generic"`
 	Sticky         bool   `yaml:"sticky"   json:"sticky,omitempty" property:"sticky"`
 	RequestTimeout string `yaml:"timeout"  json:"timeout,omitempty" property:"timeout"`
+	ForceTag       bool   `yaml:"force.tag"  json:"force.tag,omitempty" property:"force.tag"`
 }
 
 // Prefix ...
@@ -99,7 +100,9 @@ func (c *ReferenceConfig) Refer(_ interface{}) {
 		common.WithParams(c.getUrlMap()),
 		common.WithParamsValue(constant.BEAN_NAME_KEY, c.id),
 	)
-
+	if c.ForceTag {
+		cfgURL.AddParam(constant.ForceUseTag, "true")
+	}
 	if c.Url != "" {
 		// 1. user specified URL, could be peer-to-peer address, or register center's address.
 		urlStrings := gxstrings.RegSplit(c.Url, "\\s*[;]+\\s*")
diff --git a/config/condition_router_config.go b/config/router_config.go
similarity index 89%
rename from config/condition_router_config.go
rename to config/router_config.go
index 87e835108..0670ee9c2 100644
--- a/config/condition_router_config.go
+++ b/config/router_config.go
@@ -28,13 +28,14 @@ import (
 	"github.com/apache/dubbo-go/common/yaml"
 )
 
-//RouterInit Load config file to init router config
+// RouterInit Load config file to init router config
 func RouterInit(confRouterFile string) error {
 	fileRouterFactories := extension.GetFileRouterFactories()
 	bytes, err := yaml.LoadYMLConfig(confRouterFile)
 	if err != nil {
 		return perrors.Errorf("ioutil.ReadFile(file:%s) = error:%v", confRouterFile, perrors.WithStack(err))
 	}
+	logger.Warnf("get fileRouterFactories len{%+v})", len(fileRouterFactories))
 	for k, factory := range fileRouterFactories {
 		r, e := factory.NewFileRouter(bytes)
 		if e == nil {
@@ -42,7 +43,7 @@ func RouterInit(confRouterFile string) error {
 			directory.AddRouterURLSet(&url)
 			return nil
 		}
-		logger.Warnf("router config type %s create fail \n", k)
+		logger.Warnf("router config type %s create fail {%v}\n", k, e)
 	}
 	return perrors.Errorf("no file router exists for parse %s , implement router.FIleRouterFactory please.", confRouterFile)
 }
diff --git a/config/condition_router_config_test.go b/config/router_config_test.go
similarity index 100%
rename from config/condition_router_config_test.go
rename to config/router_config_test.go
diff --git a/config/service_config.go b/config/service_config.go
index 7d97fa4d1..a5ddefca6 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -69,6 +69,7 @@ type ServiceConfig struct {
 	ExecuteLimitRejectedHandler string            `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"`
 	Auth                        string            `yaml:"auth" json:"auth,omitempty" property:"auth"`
 	ParamSign                   string            `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"`
+	Tag                         string            `yaml:"tag" json:"tag,omitempty" property:"tag"`
 
 	unexported    *atomic.Bool
 	exported      *atomic.Bool
@@ -144,7 +145,9 @@ func (c *ServiceConfig) Export() error {
 			common.WithMethods(strings.Split(methods, ",")),
 			common.WithToken(c.Token),
 		)
-
+		if len(c.Tag) > 0 {
+			ivkURL.AddParam(constant.Tagkey, c.Tag)
+		}
 		if len(regUrls) > 0 {
 			for _, regUrl := range regUrls {
 				regUrl.SubURL = ivkURL
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index a6d2cdf49..20be268d7 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -127,12 +127,13 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 		} else if url.Protocol == constant.ROUTER_PROTOCOL || //2.for router
 			url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY {
 			url = nil
+
 		}
 		switch res.Action {
 		case remoting.EventTypeAdd, remoting.EventTypeUpdate:
 			logger.Infof("selector add service url{%s}", res.Service)
-			var urls []*common.URL
 
+			var urls []*common.URL
 			for _, v := range directory.GetRouterURLSet().Values() {
 				urls = append(urls, v.(*common.URL))
 			}
@@ -140,8 +141,6 @@ func (dir *registryDirectory) refreshInvokers(res *registry.ServiceEvent) {
 			if len(urls) > 0 {
 				dir.SetRouters(urls)
 			}
-
-			//dir.cacheService.EventTypeAdd(res.Path, dir.serviceTTL)
 			oldInvoker = dir.cacheInvoker(url)
 		case remoting.EventTypeDel:
 			oldInvoker = dir.uncacheInvoker(url)
-- 
GitLab