Newer
Older
/*
* 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.
*/
"github.com/RoaringBitmap/roaring"
"github.com/apache/dubbo-go/cluster/router"
"github.com/apache/dubbo-go/common/constant"
WhenCondition map[string]MatchPair
ThenCondition map[string]MatchPair
}
func NewConditionRouterWithRule(rule string) (*ConditionRouter, error) {
rule = strings.Replace(rule, "consumer.", "", -1)
rule = strings.Replace(rule, "provider.", "", -1)
i := strings.Index(rule, "=>")
if i > 0 {
whenRule = rule[0:i]
}
if i < 0 {
thenRule = rule
} else {
thenRule = rule[i+2:]
}
whenRule = strings.Trim(whenRule, " ")
thenRule = strings.Trim(thenRule, " ")
w, err := parseRule(whenRule)
if err != nil {
return nil, perrors.Errorf("%s", "")
}
t, err := parseRule(thenRule)
if err != nil {
return nil, perrors.Errorf("%s", "")
}
if len(whenRule) == 0 || "true" == whenRule {
when = make(map[string]MatchPair, 16)
if len(thenRule) == 0 || "false" == thenRule {
when = make(map[string]MatchPair, 16)
func NewConditionRouter(url *common.URL) (*ConditionRouter, error) {
if url == nil {
return nil, perrors.Errorf("Illegal route URL!")
}
rule, err := url.GetParamAndDecoded(constant.RULE_KEY)
if err != nil || len(rule) == 0 {
return nil, perrors.Errorf("Illegal route rule!")
}
router, err := NewConditionRouterWithRule(rule)
if err != nil {
return nil, err
}
router.url = url
if url.GetParam(constant.APPLICATION_KEY, "") != "" {
defaultPriority = 150

william feng
committed
} 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)
func (c *ConditionRouter) URL() *common.URL {
return c.url
// Enabled Return is condition router is enabled
// true: enabled
// false: disabled
func (c *ConditionRouter) Enabled() bool {
return c.enabled
}
func (c *ConditionRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap {
if !isMatchWhen {
return invokers
}
result := roaring.NewBitmap()
for iter := invokers.Iterator(); iter.HasNext(); {
index := iter.Next()
invoker := cache.GetInvokers()[index]
invokerUrl := invoker.GetUrl()
if isMatchThen {
result.Add(index)
}
}
logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule)
return invokers
func parseRule(rule string) (map[string]MatchPair, error) {
values := gxset.NewSet()
matches := routerPatternReg.FindAllSubmatch([]byte(rule), -1)
for _, groups := range matches {
separator := string(groups[1])
content := string(groups[2])
switch separator {
case "":
pair = MatchPair{
Matches: gxset.NewSet(),
Mismatches: gxset.NewSet(),
condition[content] = pair
case "&":
if r, ok := condition[content]; ok {
pair = r
} else {
Matches: gxset.NewSet(),
Mismatches: gxset.NewSet(),
condition[content] = pair
}
case "=":
if &pair == nil {
return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
values.Add(content)
return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
values = pair.Mismatches
values.Add(content)
if values.Empty() {
return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
values.Add(content)
return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex)
if indexTuple := routerPatternReg.FindIndex([]byte(rule)); len(indexTuple) > 0 {
func (c *ConditionRouter) MatchWhen(url *common.URL, invocation protocol.Invocation) bool {
condition := matchCondition(c.WhenCondition, url, nil, invocation)
return len(c.WhenCondition) == 0 || condition
func (c *ConditionRouter) MatchThen(url *common.URL, param *common.URL) bool {
condition := matchCondition(c.ThenCondition, url, param, nil)
return len(c.ThenCondition) > 0 && condition
func matchCondition(pairs map[string]MatchPair, url *common.URL, param *common.URL, invocation protocol.Invocation) bool {
// because url.ToMap() may return nil, but it should continue to process make condition
sample = make(map[string]string)
for key, matchPair := range pairs {
var sampleValue string
if invocation != nil && ((constant.METHOD_KEY == key) || (constant.METHOD_KEYS == key)) {
sampleValue = invocation.MethodName()
} else {
sampleValue = sample[key]
sampleValue = sample[constant.PREFIX_DEFAULT_KEY+key]
if !(matchPair.Matches.Empty()) {
// MatchPair Match key pair, condition process
Matches *gxset.HashSet
Mismatches *gxset.HashSet
func (pair MatchPair) isMatch(value string, param *common.URL) bool {
if !pair.Matches.Empty() && pair.Mismatches.Empty() {
for match := range pair.Matches.Items {
if isMatchGlobalPattern(match.(string), value, param) {
return true
}
}
return false
}
if !pair.Mismatches.Empty() && pair.Matches.Empty() {
for mismatch := range pair.Mismatches.Items {
if isMatchGlobalPattern(mismatch.(string), value, param) {
return false
}
}
return true
}
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 isMatchGlobalPattern(mismatch.(string), value, param) {
return false
}
}
for match := range pair.Matches.Items {
if isMatchGlobalPattern(match.(string), value, param) {
return true
}
}
return false
}