diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go index 8a7e0c0e8359c69faf0e504adeb1778c38a08e04..651c90ff6862bd75e604b0572bb15e583a00065e 100644 --- a/cluster/directory/base_directory.go +++ b/cluster/directory/base_directory.go @@ -85,15 +85,21 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { for _, url := range urls { routerKey := url.GetParam(constant.ROUTER_KEY, "") - if len(routerKey) > 0 { - factory := extension.GetRouterFactory(url.Protocol) - r, err := factory.NewPriorityRouter(url) - if err != nil { - logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err) - return + if len(routerKey) == 0 { + continue + } + if url.Protocol == constant.CONDITION_ROUTE_PROTOCOL { + if !dir.isProperRouter(url) { + continue } - routers = append(routers, r) } + factory := extension.GetRouterFactory(url.Protocol) + r, err := factory.NewPriorityRouter(url) + if err != nil { + logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err) + return + } + routers = append(routers, r) } logger.Infof("Init file condition router success, size: %v", len(routers)) @@ -104,6 +110,21 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { rc.AddRouters(routers) } +func (dir *BaseDirectory) isProperRouter(url *common.URL) bool { + app := url.GetParam(constant.APPLICATION_KEY, "") + serviceKey := dir.GetUrl().ServiceKey() + if serviceKey == "" { + serviceKey = dir.GetUrl().SubURL.ServiceKey() + } + if len(app) > 0 && app == dir.GetUrl().GetParam(constant.APPLICATION_KEY, "") { + return true + } + if url.ServiceKey() == serviceKey { + return true + } + return false +} + // Destroy Destroy func (dir *BaseDirectory) Destroy(doDestroy func()) { if dir.destroyed.CAS(false, true) { diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go index 8b60163b79b7120829e51f69238474a127133fb4..a2b62dfa008e6cd17b1200d93cd235da17d03905 100644 --- a/cluster/directory/base_directory_test.go +++ b/cluster/directory/base_directory_test.go @@ -37,7 +37,7 @@ import ( var ( url, _ = common.NewURL( fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) - anyUrl, _ = common.NewURL(fmt.Sprintf("condition://%s/com.foo.BarService", constant.ANYHOST_VALUE)) + anyURL, _ = common.NewURL(fmt.Sprintf("condition://%s/com.foo.BarService", constant.ANYHOST_VALUE)) ) func TestNewBaseDirectory(t *testing.T) { @@ -48,13 +48,17 @@ func TestNewBaseDirectory(t *testing.T) { } func TestBuildRouterChain(t *testing.T) { - directory := NewBaseDirectory(&url) + + regURL := url + regURL.AddParam(constant.INTERFACE_KEY, "mock-app") + directory := NewBaseDirectory(®URL) assert.NotNil(t, directory) localIP, _ := gxnet.GetLocalIP() rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) - routeURL := getRouteUrl(rule) + routeURL := getRouteURL(rule, anyURL) + routeURL.AddParam(constant.INTERFACE_KEY, "mock-app") routerURLs := make([]*common.URL, 0) routerURLs = append(routerURLs, routeURL) directory.SetRouters(routerURLs) @@ -63,9 +67,53 @@ func TestBuildRouterChain(t *testing.T) { assert.NotNil(t, chain) } -func getRouteUrl(rule string) *common.URL { - anyUrl.AddParam("rule", rule) - anyUrl.AddParam("force", "true") - anyUrl.AddParam(constant.ROUTER_KEY, "router") - return &url +func getRouteURL(rule string, u common.URL) *common.URL { + ru := u + ru.AddParam("rule", rule) + ru.AddParam("force", "true") + ru.AddParam(constant.ROUTER_KEY, "router") + return &ru +} + +func TestIsProperRouter(t *testing.T) { + regURL := url + regURL.AddParam(constant.APPLICATION_KEY, "mock-app") + d := NewBaseDirectory(®URL) + localIP, _ := gxnet.GetLocalIP() + rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) + routeURL := getRouteURL(rule, anyURL) + routeURL.AddParam(constant.APPLICATION_KEY, "mock-app") + rst := d.isProperRouter(routeURL) + assert.True(t, rst) + + regURL.AddParam(constant.APPLICATION_KEY, "") + regURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService") + d = NewBaseDirectory(®URL) + routeURL = getRouteURL(rule, anyURL) + routeURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService") + rst = d.isProperRouter(routeURL) + assert.True(t, rst) + + regURL.AddParam(constant.APPLICATION_KEY, "") + regURL.AddParam(constant.INTERFACE_KEY, "") + d = NewBaseDirectory(®URL) + routeURL = getRouteURL(rule, anyURL) + rst = d.isProperRouter(routeURL) + assert.True(t, rst) + + regURL.SetParam(constant.APPLICATION_KEY, "") + regURL.SetParam(constant.INTERFACE_KEY, "") + d = NewBaseDirectory(®URL) + routeURL = getRouteURL(rule, anyURL) + routeURL.AddParam(constant.APPLICATION_KEY, "mock-service") + rst = d.isProperRouter(routeURL) + assert.False(t, rst) + + regURL.SetParam(constant.APPLICATION_KEY, "") + regURL.SetParam(constant.INTERFACE_KEY, "") + d = NewBaseDirectory(®URL) + routeURL = getRouteURL(rule, anyURL) + routeURL.AddParam(constant.INTERFACE_KEY, "mock-service") + rst = d.isProperRouter(routeURL) + assert.False(t, rst) } diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go index c1f723525f5307e7732f0ea1ecc27eca7ba09c8d..065f0e49d9a2a465c7bb26a9541b34ad569bb93a 100644 --- a/cluster/router/chain/chain_test.go +++ b/cluster/router/chain/chain_test.go @@ -66,7 +66,9 @@ func TestNewRouterChain(t *testing.T) { err = z.Create(path) assert.NoError(t, err) - testyml := `enabled: true + testyml := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: @@ -93,7 +95,7 @@ conditions: assert.NotNil(t, appRouter) assert.NotNil(t, appRouter.RouterRule()) rule := appRouter.RouterRule() - assert.Equal(t, "", rule.Scope) + assert.Equal(t, "application", rule.Scope) assert.True(t, rule.Force) assert.True(t, rule.Enabled) assert.True(t, rule.Valid) @@ -101,7 +103,7 @@ conditions: assert.Equal(t, testyml, rule.RawRule) assert.Equal(t, false, rule.Runtime) assert.Equal(t, false, rule.Dynamic) - assert.Equal(t, "", rule.Key) + assert.Equal(t, "mock-app", rule.Key) } func TestNewRouterChainURLNil(t *testing.T) { @@ -116,7 +118,9 @@ func TestRouterChainAddRouters(t *testing.T) { err = z.Create(path) assert.NoError(t, err) - testyml := `enabled: true + testyml := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: @@ -182,7 +186,9 @@ func TestRouterChainRouteAppRouter(t *testing.T) { err = z.Create(path) assert.NoError(t, err) - testyml := `enabled: true + testyml := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go index 8b38f2dd6136b4d31f46e7214c0ad1359537b198..53465f90516fed1d5cf94b8ecc077cbfde6c743f 100644 --- a/cluster/router/condition/app_router_test.go +++ b/cluster/router/condition/app_router_test.go @@ -52,7 +52,9 @@ var ( func TestNewAppRouter(t *testing.T) { - testYML := `enabled: true + testYML := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: @@ -83,7 +85,7 @@ conditions: assert.NotNil(t, appRouter) assert.NotNil(t, appRouter.RouterRule()) rule := appRouter.RouterRule() - assert.Equal(t, "", rule.Scope) + assert.Equal(t, "application", rule.Scope) assert.True(t, rule.Force) assert.True(t, rule.Enabled) assert.True(t, rule.Valid) @@ -91,13 +93,15 @@ conditions: assert.Equal(t, testYML, rule.RawRule) assert.Equal(t, false, rule.Runtime) assert.Equal(t, false, rule.Dynamic) - assert.Equal(t, "", rule.Key) + assert.Equal(t, "mock-app", rule.Key) assert.Equal(t, 0, rule.Priority) } func TestGenerateConditions(t *testing.T) { - testYML := `enabled: true + testYML := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: @@ -135,7 +139,9 @@ conditions: func TestProcess(t *testing.T) { - testYML := `enabled: true + testYML := `scope: application +key: mock-app +enabled: true force: true runtime: false conditions: @@ -165,7 +171,8 @@ conditions: assert.Equal(t, 1, len(appRouter.conditionRouters)) - testNewYML := ` + testNewYML := `scope: application +key: mock-app enabled: true force: true runtime: false diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go index eabdf1c263446140b359b3e791238b020cecb50c..996db7443faff88d3baa1a2363f98fa62623d121 100644 --- a/cluster/router/condition/file.go +++ b/cluster/router/condition/file.go @@ -20,6 +20,7 @@ package condition import ( "encoding/base64" "net/url" + "regexp" "strconv" "strings" "sync" @@ -71,11 +72,53 @@ func (f *FileConditionRouter) URL() common.URL { common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)), common.WithParamsValue(constant.RULE_KEY, base64.URLEncoding.EncodeToString([]byte(rule))), common.WithParamsValue(constant.ROUTER_KEY, constant.CONDITION_ROUTE_PROTOCOL), - common.WithParamsValue(constant.CATEGORY_KEY, constant.ROUTERS_CATEGORY)) + common.WithParamsValue(constant.CATEGORY_KEY, constant.ROUTERS_CATEGORY), + ) + if routerRule.Scope == constant.RouterApplicationScope { + f.url.AddParam(constant.APPLICATION_KEY, routerRule.Key) + return + } + grp, srv, ver, e := parseServiceRouterKey(routerRule.Key) + if e != nil { + return + } + if len(grp) > 0 { + f.url.AddParam(constant.GROUP_KEY, grp) + } + if len(ver) > 0 { + f.url.AddParam(constant.VERSION_KEY, ver) + } + if len(srv) > 0 { + f.url.AddParam(constant.INTERFACE_KEY, srv) + } }) return f.url } +// The input value must follow [{group}/]{service}[:{version}] pattern +// the returning strings are representing group, service, version respectively. +// input: mock-group/mock-service:1.0.0 ==> "mock-group", "mock-service", "1.0.0" +// input: mock-group/mock-service ==> "mock-group", "mock-service", "" +// input: mock-service:1.0.0 ==> "", "mock-service", "1.0.0" +// For more samples, please refer to unit test. +func parseServiceRouterKey(key string) (string, string, string, error) { + if len(strings.TrimSpace(key)) == 0 { + return "", "", "", nil + } + reg := regexp.MustCompile(`(.*/{1})?([^:/]+)(:{1}[^:]*)?`) + strs := reg.FindAllStringSubmatch(key, -1) + if strs == nil || len(strs) > 1 { + return "", "", "", perrors.Errorf("Invalid key, service key must follow [{group}/]{service}[:{version}] pattern") + } + if len(strs[0]) != 4 { + return "", "", "", perrors.Errorf("Parse service router key failed") + } + grp := strings.TrimSpace(strings.TrimRight(strs[0][1], "/")) + srv := strings.TrimSpace(strs[0][2]) + ver := strings.TrimSpace(strings.TrimLeft(strs[0][3], ":")) + return grp, srv, ver, nil +} + func parseCondition(conditions []string) string { var when, then string for _, condition := range conditions { diff --git a/cluster/router/condition/file_test.go b/cluster/router/condition/file_test.go index 3092b12ff88dcacc9108c7cdd46ba1ac9f74eb2b..bd19a0d18c6692af181ffef77c5cd3f9fc16d67d 100644 --- a/cluster/router/condition/file_test.go +++ b/cluster/router/condition/file_test.go @@ -26,7 +26,9 @@ import ( ) func TestLoadYmlConfig(t *testing.T) { - router, e := NewFileConditionRouter([]byte(`priority: 1 + router, e := NewFileConditionRouter([]byte(`scope: application +key: mock-app +priority: 1 force: true conditions : - "a => b" @@ -47,12 +49,78 @@ func TestParseCondition(t *testing.T) { } func TestFileRouterURL(t *testing.T) { - router, e := NewFileConditionRouter([]byte(`priority: 1 + router, e := NewFileConditionRouter([]byte(`scope: application +key: mock-app +priority: 1 force: true conditions : - "a => b" - "c => d"`)) assert.Nil(t, e) assert.NotNil(t, router) - assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String()) + assert.Equal(t, "condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String()) + + router, e = NewFileConditionRouter([]byte(`scope: service +key: mock-service +priority: 1 +force: true +conditions : + - "a => b" + - "c => d"`)) + assert.Nil(t, e) + assert.NotNil(t, router) + assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&interface=mock-service&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D", router.URL().String()) + + router, e = NewFileConditionRouter([]byte(`scope: service +key: grp1/mock-service:v1 +priority: 1 +force: true +conditions : + - "a => b" + - "c => d"`)) + assert.Nil(t, e) + assert.NotNil(t, router) + assert.Equal(t, "condition://0.0.0.0:?category=routers&force=true&group=grp1&interface=mock-service&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D&version=v1", router.URL().String()) +} + +func TestParseServiceRouterKey(t *testing.T) { + testString := " mock-group / mock-service:1.0.0" + grp, srv, ver, err := parseServiceRouterKey(testString) + assert.Equal(t, "mock-group", grp) + assert.Equal(t, "mock-service", srv) + assert.Equal(t, "1.0.0", ver) + + testString = "mock-group/mock-service" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "mock-group", grp) + assert.Equal(t, "mock-service", srv) + assert.Equal(t, "", ver) + + testString = "mock-service:1.0.0" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "", grp) + assert.Equal(t, "mock-service", srv) + assert.Equal(t, "1.0.0", ver) + + testString = "mock-service" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "", grp) + assert.Equal(t, "mock-service", srv) + assert.Equal(t, "", ver) + + testString = "/mock-service:" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "", grp) + assert.Equal(t, "mock-service", srv) + assert.Equal(t, "", ver) + + testString = "grp:mock-service:123" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Error(t, err) + + testString = "" + grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "", grp) + assert.Equal(t, "", srv) + assert.Equal(t, "", ver) } diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go index 40a251573f5e73d40032972313565d98b288b1b1..800293da6c59f07a4bff1b6b832f482273394cec 100644 --- a/cluster/router/condition/router.go +++ b/cluster/router/condition/router.go @@ -117,7 +117,13 @@ func NewConditionRouter(url *common.URL) (*ConditionRouter, error) { } router.url = url - router.priority = url.GetParamInt(constant.RouterPriority, 0) + var defaultPriority int64 = 0 + if url.GetParam(constant.APPLICATION_KEY, "") != "" { + defaultPriority = 150 + } else if url.GetParam(constant.INTERFACE_KEY, "") != "" { + defaultPriority = 140 + } + router.priority = url.GetParamInt(constant.RouterPriority, defaultPriority) router.Force = url.GetParamBool(constant.RouterForce, false) router.enabled = url.GetParamBool(constant.RouterEnabled, true) diff --git a/cluster/router/condition/router_rule.go b/cluster/router/condition/router_rule.go index ce397d6cc0f51519123dd427709e8dba42d72a20..9f2bf2d41a941398698f110f77a7a3051a0c9c11 100644 --- a/cluster/router/condition/router_rule.go +++ b/cluster/router/condition/router_rule.go @@ -28,12 +28,13 @@ import ( 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/yaml" ) // RouterRule RouterRule config read from config file or config center type RouterRule struct { - router.BaseRouterRule `yaml:",inline""` + router.BaseRouterRule `yaml:",inline"` Conditions []string } @@ -57,7 +58,7 @@ func getRule(rawRule string) (*RouterRule, error) { return r, err } r.RawRule = rawRule - if len(r.Conditions) != 0 { + if len(r.Conditions) > 0 && len(r.Key) > 0 && (r.Scope == constant.RouterApplicationScope || r.Scope == constant.RouterServiceScope) { r.Valid = true } diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go index 675acaec912b413d8fa3d1a25463b1fd4813a7f5..369b14f08a6a650b36fe63a2d890c04fe7b892a5 100644 --- a/cluster/router/condition/router_rule_test.go +++ b/cluster/router/condition/router_rule_test.go @@ -32,6 +32,7 @@ import ( func TestGetRule(t *testing.T) { testyml := ` scope: application +key: test-provider runtime: true force: false conditions: @@ -50,10 +51,31 @@ conditions: assert.True(t, rule.Runtime) assert.Equal(t, false, rule.Force) assert.Equal(t, testyml, rule.RawRule) - assert.True(t, true, rule.Valid) + assert.True(t, rule.Valid) assert.Equal(t, false, rule.Enabled) assert.Equal(t, false, rule.Dynamic) - assert.Equal(t, "", rule.Key) + assert.Equal(t, "test-provider", rule.Key) + + testyml = ` +key: test-provider +runtime: true +force: false +conditions: + - > + method!=sayHello =>` + rule, e = getRule(testyml) + assert.Nil(t, e) + assert.False(t, rule.Valid) + + testyml = ` +scope: noApplication +key: test-provider +conditions: + - > + method!=sayHello =>` + rule, e = getRule(testyml) + assert.Nil(t, e) + assert.False(t, rule.Valid) } func TestIsMatchGlobPattern(t *testing.T) { diff --git a/cluster/router/condition/router_test.go b/cluster/router/condition/router_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c27a1d9552a331d7e67af0eb3d444c480758891e --- /dev/null +++ b/cluster/router/condition/router_test.go @@ -0,0 +1,88 @@ +/* + * 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 condition + +import ( + "testing" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/dubbogo/gost/container/set" + "github.com/stretchr/testify/assert" +) + +func TestParseRule(t *testing.T) { + testString := `` + matchPair, err := parseRule(testString) + assert.Nil(t, err) + assert.EqualValues(t, matchPair, make(map[string]MatchPair, 16)) + + testString = `method!=sayHello&application=sayGoodBye` + matchPair, err = parseRule(testString) + assert.Nil(t, err) + assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet("sayHello")) + assert.EqualValues(t, matchPair["application"].Matches, gxset.NewSet("sayGoodBye")) + + testString = `noRule` + matchPair, err = parseRule(testString) + assert.Nil(t, err) + assert.EqualValues(t, matchPair["noRule"].Mismatches, gxset.NewSet()) + assert.EqualValues(t, matchPair["noRule"].Matches, gxset.NewSet()) + + testString = `method!=sayHello,sayGoodBye` + matchPair, err = parseRule(testString) + assert.Nil(t, err) + assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet(`sayHello`, `sayGoodBye`)) + + testString = `method!=sayHello,sayGoodDay=sayGoodBye` + matchPair, err = parseRule(testString) + assert.Nil(t, err) + assert.EqualValues(t, matchPair["method"].Mismatches, gxset.NewSet(`sayHello`, `sayGoodDay`)) + assert.EqualValues(t, matchPair["method"].Matches, gxset.NewSet(`sayGoodBye`)) +} + +func TestNewConditionRouter(t *testing.T) { + url, _ := common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) + router, err := NewConditionRouter(&url) + assert.Nil(t, err) + assert.Equal(t, true, router.Enabled()) + assert.Equal(t, true, router.Force) + assert.Equal(t, int64(1), router.Priority()) + whenRule, _ := parseRule("a & c") + thenRule, _ := parseRule("b & d") + assert.EqualValues(t, router.WhenCondition, whenRule) + assert.EqualValues(t, router.ThenCondition, thenRule) + + router, err = NewConditionRouter(nil) + assert.Error(t, err) + + url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmT4gYiAmIGQ%3D`) + router, err = NewConditionRouter(&url) + assert.Error(t, err) + + url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) + router, err = NewConditionRouter(&url) + assert.Nil(t, err) + assert.Equal(t, int64(150), router.Priority()) + + url, _ = common.NewURL(`condition://0.0.0.0:?category=routers&force=true&interface=mock-service&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) + router, err = NewConditionRouter(&url) + assert.Nil(t, err) + assert.Equal(t, int64(140), router.Priority()) +} diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go index 926446dcb2f18fa2fd4639a9246a85f435d75d45..fcf5542fdd5f5cf285f4060a234cf2ef2c4185ed 100644 --- a/cluster/router/tag/router_rule.go +++ b/cluster/router/tag/router_rule.go @@ -24,7 +24,7 @@ import ( // RouterRule RouterRule config read from config file or config center type RouterRule struct { - router.BaseRouterRule `yaml:",inline""` + router.BaseRouterRule `yaml:",inline"` } func getRule(rawRule string) (*RouterRule, error) { diff --git a/common/constant/key.go b/common/constant/key.go index ea9bad9d501e7db63c69b0157e568f816ca7ba81..fb1b50db8cf565ee782a2d8aa85200b8c2595c05 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -197,7 +197,14 @@ const ( RouterEnabled = "enabled" // Priority Priority key in router module RouterPriority = "priority" - + // RouterScope Scope key in router module + RouterScope = "scope" + // RouterApplicationScope Scope key in router module + RouterApplicationScope = "application" + // RouterServiceScope Scope key in router module + RouterServiceScope = "service" + // RouterRuleKey defines the key of the router, service's/application's name + RouterRuleKey = "key" // ForceUseTag is the tag in attachment ForceUseTag = "dubbo.force.tag" Tagkey = "dubbo.tag" diff --git a/common/url.go b/common/url.go index 807d0ed5eff4ecb70d3adeb8524b841d0ec92a58..ec6dce9175596e4f1774614f8f0cb978d181f300 100644 --- a/common/url.go +++ b/common/url.go @@ -381,7 +381,7 @@ func (c URL) Service() string { if service != "" { return service } else if c.SubURL != nil { - service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) + service = c.SubURL.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if service != "" { // if url.path is "" then return suburl's path, special for registry url return service } diff --git a/common/yaml/yaml.go b/common/yaml/yaml.go index 5edda1b3c7751e8171528d121148b6c3c60fe128..d7e1ca4e898ce64f316b2abf8cb9e3324eb31e32 100644 --- a/common/yaml/yaml.go +++ b/common/yaml/yaml.go @@ -27,7 +27,7 @@ import ( "gopkg.in/yaml.v2" ) -// loadYMLConfig Load yml config byte from file +// LoadYMLConfig Load yml config byte from file func LoadYMLConfig(confProFile string) ([]byte, error) { if len(confProFile) == 0 { return nil, perrors.Errorf("application configure(provider) file name is nil") @@ -40,7 +40,7 @@ func LoadYMLConfig(confProFile string) ([]byte, error) { return ioutil.ReadFile(confProFile) } -// unmarshalYMLConfig Load yml config byte from file, then unmarshal to object +// UnmarshalYMLConfig Load yml config byte from file, then unmarshal to object func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) { confFileStream, err := LoadYMLConfig(confProFile) if err != nil { @@ -49,6 +49,12 @@ func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) { return confFileStream, yaml.Unmarshal(confFileStream, out) } +//UnmarshalYML unmarshals decodes the first document found within the in byte slice and assigns decoded values into the out value. func UnmarshalYML(data []byte, out interface{}) error { return yaml.Unmarshal(data, out) } + +// MarshalYML serializes the value provided into a YAML document. +func MarshalYML(in interface{}) ([]byte, error) { + return yaml.Marshal(in) +} diff --git a/config/router_config.go b/config/router_config.go index 16a2bec918d0f9a2de2174324e78ca21e853dabf..ed42577ed3cce2e5a1ab0da290f0d5450553d8fb 100644 --- a/config/router_config.go +++ b/config/router_config.go @@ -23,6 +23,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/common/yaml" @@ -32,16 +33,37 @@ var ( routerURLSet = gxset.NewSet() ) +// LocalRouterRules defines the local router config structure +type LocalRouterRules struct { + RouterRules []interface{} `yaml:"routerRules"` +} + // 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) + routerRules := &LocalRouterRules{} + err = yaml.UnmarshalYML(bytes, routerRules) + if err != nil { + return perrors.Errorf("Load router file %s failed due to error: %v", confRouterFile, perrors.WithStack(err)) + } + if len(routerRules.RouterRules) == 0 { + return perrors.Errorf("No router configurations in file %s", confRouterFile) + } + fileRouterFactories := extension.GetFileRouterFactories() + for _, v := range routerRules.RouterRules { + content, _ := yaml.MarshalYML(v) + err = initRouterConfig(content, fileRouterFactories) + } + return err +} + +func initRouterConfig(content []byte, factories map[string]router.FilePriorityRouterFactory) error { + logger.Warnf("get fileRouterFactories len{%+v})", len(factories)) + for k, factory := range factories { + r, e := factory.NewFileRouter(content) if e == nil { url := r.URL() routerURLSet.Add(&url) @@ -52,6 +74,7 @@ func RouterInit(confRouterFile string) error { return perrors.Errorf("no file router exists for parse %s , implement router.FIleRouterFactory please.", confRouterFile) } +// GetRouterURLSet exposes the routerURLSet func GetRouterURLSet() *gxset.HashSet { return routerURLSet } diff --git a/config/router_config_test.go b/config/router_config_test.go index 72e51c1c82562b03736fd0afef79b78d83d6f4f3..13af7056d5280ef4cca3c0f9ede9397407df7478 100644 --- a/config/router_config_test.go +++ b/config/router_config_test.go @@ -23,6 +23,7 @@ import ( ) import ( + "github.com/dubbogo/gost/container/set" "github.com/stretchr/testify/assert" ) @@ -31,6 +32,7 @@ import ( ) const testYML = "testdata/router_config.yml" +const testMultiRouterYML = "testdata/router_multi_config.yml" const errorTestYML = "testdata/router_config_error.yml" func TestString(t *testing.T) { @@ -63,4 +65,10 @@ func TestRouterInit(t *testing.T) { assert.NoError(t, errPro) assert.Equal(t, 1, routerURLSet.Size()) + + routerURLSet = gxset.NewSet() + errPro = RouterInit(testMultiRouterYML) + assert.NoError(t, errPro) + + assert.Equal(t, 2, routerURLSet.Size()) } diff --git a/config/service_config_test.go b/config/service_config_test.go index 0f7e404f6e336b8ad254e4007825ecbfcaf9d78b..d2bbda0c49ee18dfeb8e6475203f09d4c2fbb3b9 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -153,7 +153,7 @@ func TestExport(t *testing.T) { providerConfig = nil } -func TestgetRandomPort(t *testing.T) { +func TestGetRandomPort(t *testing.T) { protocolConfigs := make([]*ProtocolConfig, 0, 3) ip, err := gxnet.GetLocalIP() diff --git a/config/testdata/router_config.yml b/config/testdata/router_config.yml index f6b91f5da7d95256e6279924b884bfd450c45a08..1845650d93c152585c4d4caf60ab7aa3d5b04887 100644 --- a/config/testdata/router_config.yml +++ b/config/testdata/router_config.yml @@ -1,6 +1,9 @@ # dubbo router yaml configure file -priority: 1 -force: true -conditions : - - "a => b" - - "c => d" \ No newline at end of file +routerRules: + - scope: application + key: mock-app + priority: 1 + force: true + conditions : + - "a => b" + - "c => d" \ No newline at end of file diff --git a/config/testdata/router_config_error.yml b/config/testdata/router_config_error.yml index 37894ac96474281d10131b53d8e644f10a18b14e..74e89cc52ec772a52c3c424a94276075fb099f8c 100644 --- a/config/testdata/router_config_error.yml +++ b/config/testdata/router_config_error.yml @@ -1,6 +1,7 @@ # dubbo router yaml configure file -priority: 1 -force: true -noConditions : - - "a => b" - - "c => d" \ No newline at end of file +routerRules: + - priority: 1 + force: true + noConditions : + - "a => b" + - "c => d" \ No newline at end of file diff --git a/config/testdata/router_multi_config.yml b/config/testdata/router_multi_config.yml new file mode 100644 index 0000000000000000000000000000000000000000..42bb4cbe70a43dc82ad4a4849059ad4344e6fd85 --- /dev/null +++ b/config/testdata/router_multi_config.yml @@ -0,0 +1,16 @@ +# dubbo router yaml configure file +routerRules: + - scope: application + key: mock-app + priority: 1 + force: true + conditions : + - "a => b" + - "c => d" + - scope: application + key: mock-app2 + priority: 1 + force: true + conditions : + - "a => b" + - "c => d" \ No newline at end of file