You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2024/06/11 10:03:47 UTC

(dubbo-go) branch main updated: Design and implement new conditional rules V3.1 (#2686)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 8035430c1 Design and implement new conditional rules V3.1 (#2686)
8035430c1 is described below

commit 8035430c1b3f4d8c4e3b3ef8ff3eac26daeb8954
Author: YarBor <11...@users.noreply.github.com>
AuthorDate: Tue Jun 11 18:03:41 2024 +0800

    Design and implement new conditional rules V3.1 (#2686)
---
 cluster/router/condition/dynamic_router.go |  45 ++++---
 cluster/router/condition/route.go          | 172 +++++++++++++++++++++++---
 cluster/router/condition/router_test.go    | 190 +++++++++++++++++++----------
 common/constant/key.go                     |  47 +++----
 config/router_config.go                    |  18 ++-
 5 files changed, 352 insertions(+), 120 deletions(-)

diff --git a/cluster/router/condition/dynamic_router.go b/cluster/router/condition/dynamic_router.go
index 38e718736..6603d9bad 100644
--- a/cluster/router/condition/dynamic_router.go
+++ b/cluster/router/condition/dynamic_router.go
@@ -40,14 +40,15 @@ import (
 	"gopkg.in/yaml.v2"
 )
 
-type conditionRoute []*StateRouter
+// for version 3.0-
+type stateRouters []*StateRouter
 
-func (p conditionRoute) route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
+func (p stateRouters) route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
 	if len(invokers) == 0 || len(p) == 0 {
 		return invokers
 	}
 	for _, router := range p {
-		invokers, _ = router.Route(invokers, url, invocation)
+		invokers = router.Route(invokers, url, invocation)
 		if len(invokers) == 0 {
 			break
 		}
@@ -55,18 +56,18 @@ func (p conditionRoute) route(invokers []protocol.Invoker, url *common.URL, invo
 	return invokers
 }
 
-type multiplyConditionRoute []*StateRouter
+type multiplyConditionRoute []*MultiDestRouter
 
 func (m multiplyConditionRoute) route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
 	if len(invokers) == 0 || len(m) == 0 {
 		return invokers
 	}
 	for _, router := range m {
-		matchInvokers, isMatch := router.Route(invokers, url, invocation)
-		if !isMatch || (len(matchInvokers) == 0 && !router.force) {
+		res, isMatchWhen := router.Route(invokers, url, invocation)
+		if !isMatchWhen || (len(res) == 0 && invocation.GetAttachmentInterface(constant.TrafficDisableKey) == nil && !router.force) {
 			continue
 		}
-		return matchInvokers
+		return res
 	}
 	return []protocol.Invoker{}
 }
@@ -96,8 +97,10 @@ func (d *DynamicRouter) Route(invokers []protocol.Invoker, url *common.URL, invo
 	}
 	if cr != nil {
 		res := cr.route(invokers, url, invocation)
-		if len(res) == 0 && !force {
-			return invokers
+		if len(res) == 0 {
+			if invocation.GetAttachmentInterface(constant.TrafficDisableKey) == nil && !force {
+				return invokers
+			}
 		}
 		return res
 	} else {
@@ -176,21 +179,33 @@ func generateMultiConditionRoute(rawConfig string) (multiplyConditionRoute, bool
 		return nil, false, false, err
 	}
 
-	force, enable := routerConfig.Enabled, routerConfig.Force
+	enable, force := routerConfig.Enabled, routerConfig.Force
 	if !enable {
 		return nil, false, false, nil
 	}
 
-	conditionRouters := make([]*StateRouter, 0, len(routerConfig.Conditions))
+	conditionRouters := make([]*MultiDestRouter, 0, len(routerConfig.Conditions))
 	for _, conditionRule := range routerConfig.Conditions {
 		url, err := common.NewURL("condition://")
 		if err != nil {
 			return nil, false, false, err
 		}
-		url.AddParam(constant.RuleKey, conditionRule.Rule)
+
+		url.SetAttribute(constant.RuleKey, conditionRule)
+		url.AddParam(constant.TrafficDisableKey, strconv.FormatBool(conditionRule.Disable))
 		url.AddParam(constant.ForceKey, strconv.FormatBool(conditionRule.Force))
-		url.AddParam(constant.PriorityKey, strconv.FormatInt(int64(conditionRule.Priority), 10))
-		conditionRoute, err := NewConditionStateRouter(url)
+		if conditionRule.Priority < 0 {
+			logger.Warnf("got conditionRouteConfig.conditions.priority (%d < 0) is invalid, ignore priority value, use defatult %d ", conditionRule.Priority, constant.DefaultRoutePriority)
+		} else {
+			url.AddParam(constant.PriorityKey, strconv.FormatInt(int64(conditionRule.Priority), 10))
+		}
+		if conditionRule.Ratio < 0 || conditionRule.Ratio > 100 {
+			logger.Warnf("got conditionRouteConfig.conditions.ratio (%d) is invalid, hope (0 - 100), ignore ratio value, use defatult %d ", conditionRule.Ratio, constant.DefaultRouteRatio)
+		} else {
+			url.AddParam(constant.RatioKey, strconv.FormatInt(int64(conditionRule.Ratio), 10))
+		}
+
+		conditionRoute, err := NewConditionMultiDestRouter(url)
 		if err != nil {
 			return nil, false, false, err
 		}
@@ -203,7 +218,7 @@ func generateMultiConditionRoute(rawConfig string) (multiplyConditionRoute, bool
 	return conditionRouters, force, enable, nil
 }
 
-func generateConditionsRoute(rawConfig string) (conditionRoute, bool, bool, error) {
+func generateConditionsRoute(rawConfig string) (stateRouters, bool, bool, error) {
 	routerConfig, err := parseConditionRoute(rawConfig)
 	if err != nil {
 		logger.Warnf("[condition router]Build a new condition route config error, %s and we will use the original condition rule configuration.", err.Error())
diff --git a/cluster/router/condition/route.go b/cluster/router/condition/route.go
index 7d3074af2..55e85bdfd 100644
--- a/cluster/router/condition/route.go
+++ b/cluster/router/condition/route.go
@@ -18,6 +18,7 @@
 package condition
 
 import (
+	"math/rand"
 	"regexp"
 	"sort"
 	"strings"
@@ -48,9 +49,6 @@ var (
 )
 
 type StateRouter struct {
-	priority      int64
-	force         bool
-	url           *common.URL
 	whenCondition map[string]matcher.Matcher
 	thenCondition map[string]matcher.Matcher
 }
@@ -62,13 +60,7 @@ func NewConditionStateRouter(url *common.URL) (*StateRouter, error) {
 		return nil, errors.Errorf("No ConditionMatcherFactory exists")
 	}
 
-	force := url.GetParamBool(constant.ForceKey, false)
-	priority := url.GetParamInt(constant.PriorityKey, constant.DefaultPriority)
-	c := &StateRouter{
-		url:      url,
-		force:    force,
-		priority: priority,
-	}
+	c := &StateRouter{}
 
 	when, then, err := generateMatcher(url)
 	if err != nil {
@@ -83,18 +75,18 @@ func NewConditionStateRouter(url *common.URL) (*StateRouter, error) {
 // condition rule like `self_condition => peers_condition `
 //
 // @return active_peers_invokers, Is_self_condition_match_success
-func (s *StateRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) ([]protocol.Invoker, bool) {
+func (s *StateRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
 	if len(invokers) == 0 {
-		return invokers, false
+		return invokers
 	}
 
 	if !s.matchWhen(url, invocation) {
-		return invokers, false
+		return invokers
 	}
 
 	if len(s.thenCondition) == 0 {
 		logger.Warn("condition state router thenCondition is empty")
-		return []protocol.Invoker{}, true
+		return []protocol.Invoker{}
 	}
 
 	var result = make([]protocol.Invoker, 0, len(invokers))
@@ -104,7 +96,7 @@ func (s *StateRouter) Route(invokers []protocol.Invoker, url *common.URL, invoca
 		}
 	}
 
-	return result, true
+	return result
 }
 
 func (s *StateRouter) matchWhen(url *common.URL, invocation protocol.Invocation) bool {
@@ -307,6 +299,156 @@ func parseConditionRoute(routeContent string) (*config.RouterConfig, error) {
 	return routerConfig, nil
 }
 
+// MultiDestRouter Multiply-Destination-Router
+type MultiDestRouter struct {
+	whenCondition  map[string]matcher.Matcher
+	trafficDisable bool
+	thenCondition  []condSet
+	ratio          int
+	priority       int
+	force          bool
+}
+
+type condSet struct {
+	cond         map[string]matcher.Matcher
+	subSetWeight int
+}
+
+func newCondSet(cond map[string]matcher.Matcher, subSetWeight int) *condSet {
+	if subSetWeight <= 0 {
+		subSetWeight = constant.DefaultRouteConditionSubSetWeight
+	}
+	return &condSet{cond: cond, subSetWeight: subSetWeight}
+}
+
+type destSets struct {
+	dest []struct {
+		weight int
+		ivks   []protocol.Invoker
+	}
+	weightSum int
+}
+
+func newDestSets() *destSets {
+	return &destSets{
+		dest: make([]struct {
+			weight int
+			ivks   []protocol.Invoker
+		}, 0),
+		weightSum: 0,
+	}
+}
+
+func (s *destSets) addDest(weight int, ivks []protocol.Invoker) {
+	s.dest = append(s.dest, struct {
+		weight int
+		ivks   []protocol.Invoker
+	}{weight: weight, ivks: ivks})
+	s.weightSum += weight
+}
+
+func (s *destSets) randDest() []protocol.Invoker {
+	if len(s.dest) == 1 {
+		return s.dest[0].ivks
+	}
+	sum := rand.Intn(s.weightSum)
+	for _, d := range s.dest {
+		sum -= d.weight
+		if sum <= 0 {
+			return d.ivks
+		}
+	}
+	return nil
+}
+
+func (m MultiDestRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) ([]protocol.Invoker, bool) {
+	if len(invokers) == 0 {
+		return invokers, false
+	}
+
+	if !doMatch(url, nil, invocation, m.whenCondition, true) {
+		return invokers, false
+	}
+
+	if m.trafficDisable {
+		invocation.SetAttachment(constant.TrafficDisableKey, struct{}{})
+		return []protocol.Invoker{}, true
+	}
+
+	if len(m.thenCondition) == 0 {
+		logger.Warn("condition state router thenCondition is empty")
+		return []protocol.Invoker{}, true
+	}
+
+	destinations := newDestSets()
+	for _, condition := range m.thenCondition {
+		res := make([]protocol.Invoker, 0)
+		for _, invoker := range invokers {
+			if doMatch(invoker.GetURL(), url, nil, condition.cond, false) {
+				res = append(res, invoker)
+			}
+		}
+		if len(res) != 0 {
+			destinations.addDest(condition.subSetWeight, res)
+		}
+	}
+
+	if len(destinations.dest) != 0 {
+		res := destinations.randDest()
+		// check x% > m.ratio%
+		if len(res)*100/len(invokers) > m.ratio {
+			return res, true
+		}
+	}
+
+	return []protocol.Invoker{}, true
+}
+
+func NewConditionMultiDestRouter(url *common.URL) (*MultiDestRouter, error) {
+	once.Do(initMatcherFactories)
+
+	if len(matcherFactories) == 0 {
+		return nil, errors.Errorf("No ConditionMatcherFactory exists")
+	}
+
+	rawCondConf, ok := url.GetAttribute(constant.RuleKey)
+	if !ok {
+		return nil, errors.Errorf("Condition Router can't get the rule key")
+	}
+	condConf, ok := rawCondConf.(config.ConditionRule)
+	if !ok {
+		return nil, errors.Errorf("Condition Router get the rule key invaild , got %T", rawCondConf)
+	}
+
+	c := &MultiDestRouter{
+		whenCondition:  make(map[string]matcher.Matcher),
+		thenCondition:  make([]condSet, 0, len(condConf.To)),
+		trafficDisable: url.GetParamBool(constant.TrafficDisableKey, false),
+		ratio:          int(url.GetParamInt32(constant.RatioKey, constant.DefaultRouteRatio)),
+		priority:       int(url.GetParamInt32(constant.PriorityKey, constant.DefaultRoutePriority)),
+		force:          url.GetParamBool(constant.ForceKey, false),
+	}
+
+	m, err := parseRule(condConf.From.Match)
+	if err != nil {
+		return nil, err
+	}
+	for k, v := range m {
+		// if key same, cover
+		c.whenCondition[k] = v
+	}
+
+	for _, ruleTo := range condConf.To {
+		cond, err := parseRule(ruleTo.Match)
+		if err != nil {
+			return nil, err
+		}
+		c.thenCondition = append(c.thenCondition, *newCondSet(cond, ruleTo.Weight))
+	}
+
+	return c, nil
+}
+
 func parseMultiConditionRoute(routeContent string) (*config.ConditionRouter, error) {
 	routeDecoder := yaml.NewDecoder(strings.NewReader(routeContent))
 	routerConfig := &config.ConditionRouter{}
diff --git a/cluster/router/condition/router_test.go b/cluster/router/condition/router_test.go
index d8de7c374..fe0b66954 100644
--- a/cluster/router/condition/router_test.go
+++ b/cluster/router/condition/router_test.go
@@ -214,7 +214,7 @@ func TestRouteMatchFilter(t *testing.T) {
 			router, err := NewConditionStateRouter(url)
 			assert.Nil(t, err)
 
-			filteredInvokers, _ := router.Route(invokerList, data.comsumerURL, rpcInvocation)
+			filteredInvokers := router.Route(invokerList, data.comsumerURL, rpcInvocation)
 			resVal := len(filteredInvokers)
 			assert.Equal(t, data.wantVal, resVal)
 		})
@@ -407,7 +407,7 @@ func TestRouteReturn(t *testing.T) {
 			router, err := NewConditionStateRouter(url)
 			assert.Nil(t, err)
 
-			filterInvokers, _ := router.Route(invokers, consumerURL, rpcInvocation)
+			filterInvokers := router.Route(invokers, consumerURL, rpcInvocation)
 			resVal := len(filterInvokers)
 
 			assert.Equal(t, data.wantVal, resVal)
@@ -479,7 +479,7 @@ func TestRouteArguments(t *testing.T) {
 
 			rpcInvocation := invocation.NewRPCInvocation("getBar", arguments, nil)
 
-			filterInvokers, _ := router.Route(invokerList, consumerURL, rpcInvocation)
+			filterInvokers := router.Route(invokerList, consumerURL, rpcInvocation)
 			resVal := len(filterInvokers)
 			assert.Equal(t, data.wantVal, resVal)
 
@@ -559,7 +559,7 @@ func TestRouteAttachments(t *testing.T) {
 			router, err := NewConditionStateRouter(url)
 			assert.Nil(t, err)
 
-			filterInvokers, _ := router.Route(invokerList, consumerURL, rpcInvocation)
+			filterInvokers := router.Route(invokerList, consumerURL, rpcInvocation)
 
 			resVal := len(filterInvokers)
 			assert.Equal(t, data.wantVal, resVal)
@@ -648,7 +648,7 @@ func TestRouteRangePattern(t *testing.T) {
 			router, err := NewConditionStateRouter(url)
 			assert.Nil(t, err)
 
-			filterInvokers, _ := router.Route(invokerList, consumerURL, rpcInvocation)
+			filterInvokers := router.Route(invokerList, consumerURL, rpcInvocation)
 
 			resVal := len(filterInvokers)
 			assert.Equal(t, data.wantVal, resVal)
@@ -712,7 +712,7 @@ func TestRouteMultipleConditions(t *testing.T) {
 
 			rpcInvocation := invocation.NewRPCInvocation(method, arguments, nil)
 
-			filterInvokers, _ := router.Route(invokerList, consumerUrl, rpcInvocation)
+			filterInvokers := router.Route(invokerList, consumerUrl, rpcInvocation)
 			resVal := len(filterInvokers)
 			assert.Equal(t, data.wantVal, resVal)
 		})
@@ -867,68 +867,77 @@ func buildInvokers() []protocol.Invoker {
 func TestConditionRoutePriority(t *testing.T) {
 	ivks := buildInvokers()
 	ar := NewApplicationRouter()
-	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `
-configVersion: v3.1
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
 scope: service
-force: true
+force: false
 runtime: true
 enabled: true
-key: org.apache.dubbo.samples.CommentService
-conditionAction : true 
+key: shop
 conditions:
-  - rule: method=getComment & env=gray => region=Hangzhou & env=gray
-    priority: 3
-  - rule: method=getComment & env=gray => region=beijing & env=gray
-    priority: 3
-  - rule: method=getComment & env=gray => region=$region & env=gray 
-    priority: 3
-  - rule: method=getComment & env=normal => region=beijing 
-    priority: 3
-  - rule: method=getComment => region=$region ######### match here
-    priority: 30
-  - rule: method=echo => region=$region
-  - rule: method=echo =>
-    force: true
+  - from:
+      match:
+    to:
+      - match: region=$region & version=v1
+      - match: region=$region & version=v2
+        weight: 200
+      - match: region=$region & version=v3
+        weight: 300
+    force: false
+    ratio: 20
+    priority: 20
+  - from: 
+      match:
+        region=beijing & version=v1
+    to:
+      - match: env=$env & region=beijing
+    force: false
+    priority: 100
 `, ConfigType: remoting.EventTypeUpdate})
-	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
+	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing&version=v1")
 	if err != nil {
 		panic(err)
 	}
 	got := ar.Route(ivks, consumerUrl, invocation.NewRPCInvocation("getComment", nil, nil))
 	expLen := 0
 	for _, ivk := range ivks {
-		if ivk.GetURL().GetParam("region", "") == "beijing" {
+		if ivk.GetURL().GetParam("region", "") == "beijing" && "gray" == ivk.GetURL().GetParam("env", "") {
 			expLen++
 		}
 	}
+	if len(ivks)*100/expLen <= 20 {
+		expLen = 0
+	}
 	assert.Equal(t, expLen, len(got))
 }
 
 func TestConditionRouteTrafficDisable(t *testing.T) {
 	ivks := buildInvokers()
 	ar := NewApplicationRouter()
-	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `
-configVersion: v3.1
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
 scope: service
 force: true
 runtime: true
 enabled: true
-key: org.apache.dubbo.samples.CommentService
-conditionAction : true 
+key: shop
 conditions:
-  - rule: method=getComment & env=gray => region=Hangzhou & env=gray
-    priority: 3
-  - rule: method=getComment & env=gray => region=beijing & env=gray
-    priority: 3
-  - rule: method=getComment & env=gray => region=$region & env=gray 
-    priority: 3
-  - rule: method=getComment & env=normal => region=beijing 
-    priority: 3
-  - rule: method=getComment => region=$region 
-    priority: 30
-  - rule: method=echo =>
+  - from:
+      match:
+    to:
+      - match: region=$region & version=v1
+      - match: region=$region & version=v2
+        weight: 200
+      - match: region=$region & version=v3
+        weight: 300
+    force: false
+    ratio: 20
+    priority: 20
+  - from: 
+      match:
+        region=beijing & version=v1
+    to:
     force: true
-  - rule: method=echo => region=$region 
+    ratio: 20 
+    priority: 100
 `, ConfigType: remoting.EventTypeUpdate})
 	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
 	if err != nil {
@@ -941,23 +950,17 @@ conditions:
 func TestConditionRouteRegionPriority(t *testing.T) {
 	ivks := buildInvokers()
 	ar := NewApplicationRouter()
-	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `
-configVersion: v3.1
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
 scope: service
 force: true
 runtime: true
 enabled: true
-key: org.apache.dubbo.samples.CommentService
-conditionAction : true 
+key: shop
 conditions:
-  - rule: => region=$region & env=$env
-  - rule: method=getComment & env=gray => env=$env
-  - rule: method=getComment & env=gray & region=beijing => region=beijing & env=gray
-  - rule: method=getComment & env=gray => region=$region & env=gray 
-  - rule: method=getComment & env=normal => region=beijing 
-  - rule: method=echo =>
-    force: true
-  - rule: method=echo => region=$region 
+  - from:
+      match:
+    to:
+      - match: region=$region & env=$env
 `, ConfigType: remoting.EventTypeUpdate})
 	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
 	if err != nil {
@@ -992,33 +995,88 @@ conditions:
 	got = ar.Route(ivks, consumerUrl, invocation.NewRPCInvocation("getComment", nil, nil))
 	expLen = 0
 	for _, ivk := range ivks {
-		if ivk.GetURL().GetRawParam("region") == "beijing" {
+		if ivk.GetURL().GetRawParam("region") == consumerUrl.GetRawParam("region") &&
+			ivk.GetURL().GetRawParam("env") == consumerUrl.GetRawParam("env") {
 			expLen++
 		}
 	}
 	assert.Equal(t, expLen, len(got))
 }
 
+func TestConditionRouteRegionPriorityFail(t *testing.T) {
+	ivks := buildInvokers()
+	ar := NewApplicationRouter()
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
+scope: service
+force: true
+runtime: true
+enabled: true
+key: shop
+conditions:
+  - from:
+      match:
+    to:
+      - match: region=$region & env=$env
+    ratio: 100
+`, ConfigType: remoting.EventTypeUpdate})
+	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
+	if err != nil {
+		panic(err)
+	}
+	got := ar.Route(ivks, consumerUrl, invocation.NewRPCInvocation("getComment", nil, nil))
+	assert.Equal(t, 0, len(got))
+}
+
 func TestConditionRouteMatchFail(t *testing.T) {
 	ivks := buildInvokers()
 	ar := NewApplicationRouter()
-	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `
-configVersion: v3.1
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
 scope: service
 force: false
 runtime: true
 enabled: true
-key: org.apache.dubbo.samples.CommentService
-conditionAction : true 
+key: shop
 conditions:
-  - rule: => region=$region & env=$env & errTag=errTag
-  - rule: method=getComment & env=gray => env=$env
-  - rule: method=getComment & env=gray & region=beijing => region=beijing & env=gray
-  - rule: method=getComment & env=gray => region=$region & env=gray 
-  - rule: method=getComment & env=normal => region=beijing 
-  - rule: method=echo =>
+  - from:
+      match:
+    to:
+      - match: region=$region & env=$env & err-tag=Err-tag
+  - from:
+      match:
+    trafficDisable: true
+    to:
+      - match:
+`, ConfigType: remoting.EventTypeUpdate})
+	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
+	if err != nil {
+		panic(err)
+	}
+	got := ar.Route(ivks, consumerUrl, invocation.NewRPCInvocation("errMethod", nil, nil))
+	assert.Equal(t, 0, len(got))
+}
+
+func TestConditionRouteBanSpecialTraffic(t *testing.T) {
+	ivks := buildInvokers()
+	ar := NewApplicationRouter()
+	ar.Process(&config_center.ConfigChangeEvent{Key: "", Value: `configVersion: v3.1
+scope: service
+force: true
+runtime: true
+enabled: true
+key: shop
+conditions:
+  - from:
+      match: env=gray
+    to:
+      - match: 
+    force: true
+    priority: 100
+  - from:
+      match: 
+    to:
+      - match: 
     force: true
-  - rule: method=echo => region=$region 
+    priority: 100
 `, ConfigType: remoting.EventTypeUpdate})
 	consumerUrl, err := common.NewURL("consumer://127.0.0.1/com.foo.BarService?env=gray&region=beijing")
 	if err != nil {
diff --git a/common/constant/key.go b/common/constant/key.go
index 1e9959947..6ffe0642c 100644
--- a/common/constant/key.go
+++ b/common/constant/key.go
@@ -308,27 +308,32 @@ const (
 
 // Use for router module
 const (
-	ScriptRouterRuleSuffix           = ".script-router"
-	TagRouterRuleSuffix              = ".tag-router"
-	ConditionRouterRuleSuffix        = ".condition-router" // Specify condition router suffix
-	MeshRouteSuffix                  = ".MESHAPPRULE"      // Specify mesh router suffix
-	ForceUseTag                      = "dubbo.force.tag"   // the tag in attachment
-	ForceUseCondition                = "dubbo.force.condition"
-	Tagkey                           = "dubbo.tag" // key of tag
-	ConditionKey                     = "dubbo.condition"
-	AttachmentKey                    = DubboCtxKey("attachment") // key in context in invoker
-	TagRouterFactoryKey              = "tag"
-	ConditionAppRouterFactoryKey     = "provider.condition"
-	ConditionServiceRouterFactoryKey = "service.condition"
-	ScriptRouterFactoryKey           = "consumer.script"
-	ForceKey                         = "force"
-	PriorityKey                      = "priority"
-	Arguments                        = "arguments"
-	Attachments                      = "attachments"
-	Param                            = "param"
-	Scope                            = "scope"
-	Wildcard                         = "wildcard"
-	MeshRouterFactoryKey             = "mesh"
+	ScriptRouterRuleSuffix            = ".script-router"
+	TagRouterRuleSuffix               = ".tag-router"
+	ConditionRouterRuleSuffix         = ".condition-router" // Specify condition router suffix
+	MeshRouteSuffix                   = ".MESHAPPRULE"      // Specify mesh router suffix
+	ForceUseTag                       = "dubbo.force.tag"   // the tag in attachment
+	ForceUseCondition                 = "dubbo.force.condition"
+	Tagkey                            = "dubbo.tag" // key of tag
+	ConditionKey                      = "dubbo.condition"
+	AttachmentKey                     = DubboCtxKey("attachment") // key in context in invoker
+	TagRouterFactoryKey               = "tag"
+	ConditionAppRouterFactoryKey      = "provider.condition"
+	ConditionServiceRouterFactoryKey  = "service.condition"
+	ScriptRouterFactoryKey            = "consumer.script"
+	ForceKey                          = "force"
+	TrafficDisableKey                 = "trafficDisable"
+	PriorityKey                       = "priority"
+	RatioKey                          = "RatioKey"
+	Arguments                         = "arguments"
+	Attachments                       = "attachments"
+	Param                             = "param"
+	Scope                             = "scope"
+	Wildcard                          = "wildcard"
+	MeshRouterFactoryKey              = "mesh"
+	DefaultRouteRatio                 = 0
+	DefaultRouteConditionSubSetWeight = 100
+	DefaultRoutePriority              = 0
 )
 
 // Auth filter
diff --git a/config/router_config.go b/config/router_config.go
index 3cfff9cad..e8f155789 100644
--- a/config/router_config.go
+++ b/config/router_config.go
@@ -50,9 +50,21 @@ type Tag struct {
 }
 
 type ConditionRule struct {
-	Rule     string `validate:"required" yaml:"rule" json:"rule,omitempty" property:"rule"`
-	Priority int    `default:"0" yaml:"priority" json:"priority,omitempty" property:"priority"`
-	Force    bool   `default:"false" yaml:"force" json:"force,omitempty" property:"force"`
+	Priority int               `default:"0" yaml:"priority" json:"priority,omitempty" property:"priority"`
+	From     ConditionRuleFrom `yaml:"from" json:"from,omitempty" property:"from"`
+	Disable  bool              `default:"false" yaml:"trafficDisable" json:"trafficDisable,omitempty" property:"trafficDisable"`
+	To       []ConditionRuleTo `yaml:"to" json:"to,omitempty" property:"to"`
+	Ratio    int               `default:"0" yaml:"ratio" json:"ratio,omitempty" property:"priority"`
+	Force    bool              `default:"false" yaml:"force" json:"force,omitempty" property:"force"`
+}
+
+type ConditionRuleFrom struct {
+	Match string `yaml:"match" json:"match,omitempty" property:"match"`
+}
+
+type ConditionRuleTo struct {
+	Match  string `yaml:"match" json:"match,omitempty" property:"match"`
+	Weight int    `default:"100" yaml:"weight" json:"weight,omitempty" property:"weight"`
 }
 
 // ConditionRouter -- when RouteConfigVersion == v3.1, decode by this