diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go
index bd817af36c8c144295479fb07ada9411f4115bbc..e99307625baf34fa6b744f168ff4e6cb8e042502 100644
--- a/cluster/router/condition/app_router_test.go
+++ b/cluster/router/condition/app_router_test.go
@@ -113,7 +113,7 @@ conditions:
 	assert.Nil(t, err)
 	assert.NotNil(t, appRouter)
 
-	rule, err := Parse(testYML)
+	rule, err := getRule(testYML)
 	assert.Nil(t, err)
 	appRouter.generateConditions(rule)
 
diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go
index efeec53efc840d93c4b6906adfd19820a57b36fd..b2c876690043d18a1a9e746fee13f06c77a0de03 100644
--- a/cluster/router/condition/file.go
+++ b/cluster/router/condition/file.go
@@ -44,7 +44,7 @@ type FileConditionRouter struct {
 // NewFileConditionRouter Create file condition router instance with content ( from config file)
 func NewFileConditionRouter(content []byte) (*FileConditionRouter, error) {
 	fileRouter := &FileConditionRouter{}
-	rule, err := Parse(string(content))
+	rule, err := getRule(string(content))
 	if err != nil {
 		return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err))
 	}
diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go
index ba2fbb0eb2f482dfde215c1b078ecad60e66bc14..4ccc19e95521d03ae1f663ec276646cf30926533 100644
--- a/cluster/router/condition/listenable_router.go
+++ b/cluster/router/condition/listenable_router.go
@@ -102,7 +102,7 @@ func (l *listenableRouter) Process(event *config_center.ConfigChangeEvent) {
 		return
 	}
 
-	routerRule, err := Parse(content)
+	routerRule, err := getRule(content)
 	if err != nil {
 		logger.Errorf("Parse condition router rule fail,error:[%s] ", err)
 		return
diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go
index c5d46444bde921386d14a8be7eb0a89d855f8ece..0267a3c7a462acb43f84ccb4701247147699804a 100644
--- a/cluster/router/condition/router.go
+++ b/cluster/router/condition/router.go
@@ -27,7 +27,6 @@ import (
 )
 
 import (
-	matcher "github.com/apache/dubbo-go/cluster/router/match"
 	"github.com/apache/dubbo-go/common"
 	"github.com/apache/dubbo-go/common/constant"
 	"github.com/apache/dubbo-go/common/logger"
@@ -301,7 +300,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Matches.Empty() && pair.Mismatches.Empty() {
 
 		for match := range pair.Matches.Items {
-			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
+			if isMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
@@ -310,7 +309,7 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Mismatches.Empty() && pair.Matches.Empty() {
 
 		for mismatch := range pair.Mismatches.Items {
-			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
+			if isMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
@@ -319,12 +318,12 @@ func (pair MatchPair) isMatch(value string, param *common.URL) bool {
 	if !pair.Mismatches.Empty() && !pair.Matches.Empty() {
 		//when both mismatches and matches contain the same value, then using mismatches first
 		for mismatch := range pair.Mismatches.Items {
-			if matcher.IsMatchGlobalPattern(mismatch.(string), value, param) {
+			if isMatchGlobalPattern(mismatch.(string), value, param) {
 				return false
 			}
 		}
 		for match := range pair.Matches.Items {
-			if matcher.IsMatchGlobalPattern(match.(string), value, param) {
+			if isMatchGlobalPattern(match.(string), value, param) {
 				return true
 			}
 		}
diff --git a/cluster/router/condition/router_rule.go b/cluster/router/condition/router_rule.go
index 1374cf9de2585f78a27e3de99f356c6900268927..ce397d6cc0f51519123dd427709e8dba42d72a20 100644
--- a/cluster/router/condition/router_rule.go
+++ b/cluster/router/condition/router_rule.go
@@ -18,11 +18,17 @@
 package condition
 
 import (
-	"gopkg.in/yaml.v2"
+	"strings"
+)
+
+import (
+	gxstrings "github.com/dubbogo/gost/strings"
 )
 
 import (
 	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/common/yaml"
 )
 
 // RouterRule RouterRule config read from config file or config center
@@ -44,9 +50,9 @@ type RouterRule struct {
  *     =>
  *     1.1.1.1
  */
-func Parse(rawRule string) (*RouterRule, error) {
+func getRule(rawRule string) (*RouterRule, error) {
 	r := &RouterRule{}
-	err := yaml.Unmarshal([]byte(rawRule), r)
+	err := yaml.UnmarshalYML([]byte(rawRule), r)
 	if err != nil {
 		return r, err
 	}
@@ -57,3 +63,11 @@ func Parse(rawRule string) (*RouterRule, error) {
 
 	return r, nil
 }
+
+// isMatchGlobalPattern Match value to param content by pattern
+func isMatchGlobalPattern(pattern string, value string, param *common.URL) bool {
+	if param != nil && strings.HasPrefix(pattern, "$") {
+		pattern = param.GetRawParam(pattern[1:])
+	}
+	return gxstrings.IsMatchPattern(pattern, value)
+}
diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go
index 5acc7283917a7aa662b60cd90daba89d312db0cd..675acaec912b413d8fa3d1a25463b1fd4813a7f5 100644
--- a/cluster/router/condition/router_rule_test.go
+++ b/cluster/router/condition/router_rule_test.go
@@ -20,11 +20,16 @@ package condition
 import (
 	"testing"
 )
+
 import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestParse(t *testing.T) {
+import (
+	"github.com/apache/dubbo-go/common"
+)
+
+func TestGetRule(t *testing.T) {
 	testyml := `
 scope: application
 runtime: true
@@ -36,7 +41,7 @@ conditions:
     ip=127.0.0.1
     =>
     1.1.1.1`
-	rule, e := Parse(testyml)
+	rule, e := getRule(testyml)
 
 	assert.Nil(t, e)
 	assert.NotNil(t, rule)
@@ -50,3 +55,8 @@ conditions:
 	assert.Equal(t, false, rule.Dynamic)
 	assert.Equal(t, "", rule.Key)
 }
+
+func TestIsMatchGlobPattern(t *testing.T) {
+	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
+	assert.Equal(t, true, isMatchGlobalPattern("$key", "value", &url))
+}
diff --git a/cluster/router/match/match_utils.go b/cluster/router/match/match_utils.go
deleted file mode 100644
index 28fe7151c5126c41fbadf9f4d54da2b9df74a7fe..0000000000000000000000000000000000000000
--- a/cluster/router/match/match_utils.go
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package match
-
-import (
-	"strings"
-)
-
-import (
-	"github.com/apache/dubbo-go/common"
-)
-
-// IsMatchGlobalPattern Match value to param content by pattern
-func IsMatchGlobalPattern(pattern string, value string, param *common.URL) bool {
-	if param != nil && strings.HasPrefix(pattern, "$") {
-		pattern = param.GetRawParam(pattern[1:])
-	}
-	return isMatchInternalPattern(pattern, value)
-}
-
-func isMatchInternalPattern(pattern string, value string) bool {
-	if "*" == pattern {
-		return true
-	}
-	if len(pattern) == 0 && len(value) == 0 {
-		return true
-	}
-	if len(pattern) == 0 || len(value) == 0 {
-		return false
-	}
-	i := strings.LastIndex(pattern, "*")
-	switch i {
-	case -1:
-		// doesn't find "*"
-		return value == pattern
-	case len(pattern) - 1:
-		// "*" is at the end
-		return strings.HasPrefix(value, pattern[0:i])
-	case 0:
-		// "*" is at the beginning
-		return strings.HasSuffix(value, pattern[i+1:])
-	default:
-		// "*" is in the middle
-		prefix := pattern[0:1]
-		suffix := pattern[i+1:]
-		return strings.HasPrefix(value, prefix) && strings.HasSuffix(value, suffix)
-	}
-}
diff --git a/cluster/router/router.go b/cluster/router/router.go
index a28002a09e3b7217549b896d452f70997504ac8f..9ee1154437e6fd205f08098deabb1ca260c3c040 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 0000000000000000000000000000000000000000..d74924c89862ae4f4cd85b59c7008880298c0c99
--- /dev/null
+++ b/cluster/router/tag/factory.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 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{}
+
+// NewTagRouterFactory create a tagRouterFactory
+func NewTagRouterFactory() router.RouterFactory {
+	return &tagRouterFactory{}
+}
+
+// NewRouter create a tagRouter by tagRouterFactory with a url
+// The url contains router configuration information
+func (c *tagRouterFactory) NewRouter(url *common.URL) (router.Router, error) {
+	return NewTagRouter(url)
+}
+
+// NewFileRouter create a tagRouter by profile content
+func (c *tagRouterFactory) NewFileRouter(content []byte) (router.Router, error) {
+	return NewFileTagRouter(content)
+}
diff --git a/cluster/router/match/match_utils_test.go b/cluster/router/tag/factory_test.go
similarity index 55%
rename from cluster/router/match/match_utils_test.go
rename to cluster/router/tag/factory_test.go
index f16480f1d3b7dd5ca820c81d5d04d837c129687f..58bff5b18113d69f97ec513e393aa6759a3cf050 100644
--- a/cluster/router/match/match_utils_test.go
+++ b/cluster/router/tag/factory_test.go
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package match
+package tag
 
 import (
 	"testing"
@@ -29,18 +29,11 @@ import (
 	"github.com/apache/dubbo-go/common"
 )
 
-func TestIsMatchInternalPattern(t *testing.T) {
-	assert.Equal(t, true, isMatchInternalPattern("*", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("", ""))
-	assert.Equal(t, false, isMatchInternalPattern("", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("value", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("v*", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("*ue", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("*e", "value"))
-	assert.Equal(t, true, isMatchInternalPattern("v*e", "value"))
-}
-
-func TestIsMatchGlobPattern(t *testing.T) {
-	url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e")
-	assert.Equal(t, true, IsMatchGlobalPattern("$key", "value", &url))
+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 0000000000000000000000000000000000000000..8144c83203dbe98778dd6bb8dcdb9888be664b3b
--- /dev/null
+++ b/cluster/router/tag/file.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 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
+}
+
+// NewFileTagRouter Create file tag router instance with content ( from config file)
+func NewFileTagRouter(content []byte) (*FileTagRouter, error) {
+	fileRouter := &FileTagRouter{}
+	rule, err := getRule(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.router.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/file_test.go b/cluster/router/tag/file_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..94fcf9e0e0fabed2445417d14b711f91b65b9e5e
--- /dev/null
+++ b/cluster/router/tag/file_test.go
@@ -0,0 +1,62 @@
+/*
+ * 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/constant"
+)
+
+func TestNewFileTagRouter(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	assert.Equal(t, 100, router.routerRule.Priority)
+	assert.Equal(t, true, router.routerRule.Force)
+}
+
+func TestFileTagRouter_URL(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	url := router.URL()
+	assert.NotNil(t, url)
+	force := url.GetParam(constant.ForceUseTag, "false")
+	priority := url.GetParam(constant.RouterPriority, "0")
+	assert.Equal(t, "true", force)
+	assert.Equal(t, "100", priority)
+
+}
+
+func TestFileTagRouter_Priority(t *testing.T) {
+	router, e := NewFileTagRouter([]byte(`priority: 100
+force: true`))
+	assert.Nil(t, e)
+	assert.NotNil(t, router)
+	priority := router.Priority()
+	assert.Equal(t, int64(100), priority)
+}
diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go
new file mode 100644
index 0000000000000000000000000000000000000000..926446dcb2f18fa2fd4639a9246a85f435d75d45
--- /dev/null
+++ b/cluster/router/tag/router_rule.go
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"github.com/apache/dubbo-go/cluster/router"
+	"github.com/apache/dubbo-go/common/yaml"
+)
+
+// RouterRule RouterRule config read from config file or config center
+type RouterRule struct {
+	router.BaseRouterRule `yaml:",inline""`
+}
+
+func getRule(rawRule string) (*RouterRule, error) {
+	r := &RouterRule{}
+	err := yaml.UnmarshalYML([]byte(rawRule), r)
+	if err != nil {
+		return r, err
+	}
+	r.RawRule = rawRule
+	return r, nil
+}
diff --git a/cluster/router/tag/router_rule_test.go b/cluster/router/tag/router_rule_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..2df65193f9d0cf607258f3080e22b42cd6e9b16a
--- /dev/null
+++ b/cluster/router/tag/router_rule_test.go
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package tag
+
+import (
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetRule(t *testing.T) {
+	yml := `
+scope: application
+runtime: true
+force: true
+`
+	rule, e := getRule(yml)
+	assert.Nil(t, e)
+	assert.NotNil(t, rule)
+	assert.Equal(t, true, rule.Force)
+	assert.Equal(t, true, rule.Runtime)
+	assert.Equal(t, "application", rule.Scope)
+}
diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go
new file mode 100644
index 0000000000000000000000000000000000000000..87da418943e90c63a905f35260ada7880d6f51b9
--- /dev/null
+++ b/cluster/router/tag/tag_router.go
@@ -0,0 +1,94 @@
+/*
+ * 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 (
+	"strconv"
+)
+
+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, 8)
+		for _, v := range invokers {
+			if v.GetUrl().GetParam(constant.Tagkey, "") == tag {
+				result = append(result, v)
+			}
+		}
+		if len(result) == 0 && !isForceUseTag(url, invocation) {
+			return invokers
+		}
+		return result
+	}
+	return invokers
+}
+
+func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool {
+	if b, e := strconv.ParseBool(invocation.AttachmentsByKey(constant.ForceUseTag, url.GetParam(constant.ForceUseTag, "false"))); e == nil {
+		return b
+	}
+	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 0000000000000000000000000000000000000000..280b56c8ccb69eb5d32dae2369bdc862adb8e6fd
--- /dev/null
+++ b/cluster/router/tag/tag_router_test.go
@@ -0,0 +1,147 @@
+/*
+ * 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"
+	"testing"
+)
+
+import (
+	"github.com/stretchr/testify/assert"
+)
+
+import (
+	"github.com/apache/dubbo-go/common"
+	"github.com/apache/dubbo-go/protocol"
+	"github.com/apache/dubbo-go/protocol/invocation"
+)
+
+// MockInvoker is only mock the Invoker to support test tagRouter
+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))
+	inv.SetAttachments("dubbo.force.tag", "false")
+	inv.SetAttachments("dubbo.tag", "guangzhou")
+	invRst3 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 3, len(invRst3))
+}
+
+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))
+	inv.SetAttachments("dubbo.force.tag", "false")
+	invRst2 := tagRouter.Route(invokers, &u1, inv)
+	assert.Equal(t, 3, len(invRst2))
+}
diff --git a/common/constant/key.go b/common/constant/key.go
index 016c93a52f64222c9f390bef5428f143f9950111..8c84a2a8884d909bdd743f738f3d69dde399cc22 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -107,6 +107,7 @@ const (
 	ROUTERS_CATEGORY         = "routers"
 	ROUTE_PROTOCOL           = "route"
 	CONDITION_ROUTE_PROTOCOL = "condition"
+	TAG_ROUTE_PROTOCOL       = "tag"
 	PROVIDERS_CATEGORY       = "providers"
 	ROUTER_KEY               = "router"
 )
@@ -163,7 +164,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"
 
@@ -173,6 +175,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 70d71dfa859b996030c865775a588da20039f9a5..1339228618def41ccebc8d54cdebb5a623e605fa 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/common/yaml/yaml.go b/common/yaml/yaml.go
index 7c31d71c35fff547d2ed0a765e8245717148a451..93ebb166144510236aff27a67422a6377ccb5c9f 100644
--- a/common/yaml/yaml.go
+++ b/common/yaml/yaml.go
@@ -48,3 +48,7 @@ func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) {
 	}
 	return confFileStream, yaml.Unmarshal(confFileStream, out)
 }
+
+func UnmarshalYML(data []byte, out interface{}) error {
+	return yaml.Unmarshal(data, out)
+}
diff --git a/common/yaml/yaml_test.go b/common/yaml/yaml_test.go
index 45eee59048c1c074b9c386e26cc7a2252896e6ea..c8b8258a68951a1437ac2e617c13ee5af4b3a5ee 100644
--- a/common/yaml/yaml_test.go
+++ b/common/yaml/yaml_test.go
@@ -46,6 +46,18 @@ func TestUnmarshalYMLConfig_Error(t *testing.T) {
 	assert.Error(t, err)
 }
 
+func TestUnmarshalYML(t *testing.T) {
+	c := &Config{}
+	b, err := LoadYMLConfig("./testdata/config.yml")
+	assert.NoError(t, err)
+	err = UnmarshalYML(b, c)
+	assert.NoError(t, err)
+	assert.Equal(t, "strTest", c.StrTest)
+	assert.Equal(t, 11, c.IntTest)
+	assert.Equal(t, false, c.BooleanTest)
+	assert.Equal(t, "childStrTest", c.ChildConfig.StrTest)
+}
+
 type Config struct {
 	StrTest     string      `yaml:"strTest" default:"default" json:"strTest,omitempty" property:"strTest"`
 	IntTest     int         `default:"109"  yaml:"intTest" json:"intTest,omitempty" property:"intTest"`
diff --git a/config/reference_config.go b/config/reference_config.go
index 9f6030e8d7e143935dfce961971d9c8a49aa7587..b9f3da13527c4c7de851dac9e5cd8ecf4638758b 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 87e835108efd2ccac4f829386ec44a3916339f85..0670ee9c20f618021d1d574344a0df85d837bd66 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 faa8dc9f8162511db7f41592e5ef7cf064930f12..5853146aa88547f25c1a4be814ba971029ba941c 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/go.mod b/go.mod
index 83091cf8b985d2dec3d1d53967593fc9dee0d17e..67ed1eb402c34eecc5214411531b0a4ab68fda0c 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/creasty/defaults v1.3.0
 	github.com/dubbogo/getty v1.3.3
 	github.com/dubbogo/go-zookeeper v1.0.0
-	github.com/dubbogo/gost v1.5.2
+	github.com/dubbogo/gost v1.8.0
 	github.com/emicklei/go-restful/v3 v3.0.0
 	github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect
 	github.com/go-errors/errors v1.0.1 // indirect
diff --git a/go.sum b/go.sum
index 813496b6eea7838756e7f62f2b31aa58cd858356..bbbaf34640b63d6b7c423d8c7f9c4419025842cf 100644
--- a/go.sum
+++ b/go.sum
@@ -113,6 +113,8 @@ github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4D
 github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8=
 github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
+github.com/dubbogo/gost v1.8.0 h1:9ACbQe5OwMjqtinQcNJC5xp16kky27OsfSGw5L9A6vw=
+github.com/dubbogo/gost v1.8.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8=
 github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M=
 github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo=
 github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y=
diff --git a/registry/directory/directory.go b/registry/directory/directory.go
index a6d2cdf49b0935b2402e03208d1ff5f702e1cc52..20be268d7401976ef1b7884f2a7bd40eeacb8158 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)