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)