dubbo.apache.org/dubbo-go/v3@v3.1.1/cluster/router/condition/route.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package condition
    19  
    20  import (
    21  	"regexp"
    22  	"sort"
    23  	"strings"
    24  	"sync"
    25  )
    26  
    27  import (
    28  	"github.com/dubbogo/gost/log/logger"
    29  
    30  	"github.com/pkg/errors"
    31  
    32  	"gopkg.in/yaml.v2"
    33  )
    34  
    35  import (
    36  	"dubbo.apache.org/dubbo-go/v3/cluster/router/condition/matcher"
    37  	"dubbo.apache.org/dubbo-go/v3/common"
    38  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    39  	"dubbo.apache.org/dubbo-go/v3/config"
    40  	"dubbo.apache.org/dubbo-go/v3/protocol"
    41  )
    42  
    43  var (
    44  	routePattern = regexp.MustCompile("([&!=,]*)\\s*([^&!=,\\s]+)")
    45  
    46  	illegalMsg = "Illegal route rule \"%s\", The error char '%s' before '%s'"
    47  
    48  	matcherFactories = make([]matcher.ConditionMatcherFactory, 0, 8)
    49  
    50  	once sync.Once
    51  )
    52  
    53  type StateRouter struct {
    54  	enable        bool
    55  	force         bool
    56  	url           *common.URL
    57  	whenCondition map[string]matcher.Matcher
    58  	thenCondition map[string]matcher.Matcher
    59  }
    60  
    61  func NewConditionStateRouter(url *common.URL) (*StateRouter, error) {
    62  	once.Do(initMatcherFactories)
    63  
    64  	if len(matcherFactories) == 0 {
    65  		return nil, errors.Errorf("No ConditionMatcherFactory exists")
    66  	}
    67  
    68  	force := url.GetParamBool(constant.ForceKey, false)
    69  	enable := url.GetParamBool(constant.EnabledKey, true)
    70  	c := &StateRouter{
    71  		url:    url,
    72  		force:  force,
    73  		enable: enable,
    74  	}
    75  
    76  	if enable {
    77  		when, then, err := generateMatcher(url)
    78  		if err != nil {
    79  			return nil, err
    80  		}
    81  		c.whenCondition = when
    82  		c.thenCondition = then
    83  	}
    84  	return c, nil
    85  }
    86  
    87  // Route Determine the target invokers list.
    88  func (s *StateRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
    89  	if !s.enable {
    90  		return invokers
    91  	}
    92  
    93  	if len(invokers) == 0 {
    94  		return invokers
    95  	}
    96  
    97  	if !s.matchWhen(url, invocation) {
    98  		return invokers
    99  	}
   100  
   101  	if len(s.thenCondition) == 0 {
   102  		logger.Warn("condition state router thenCondition is empty")
   103  		return []protocol.Invoker{}
   104  	}
   105  
   106  	var result = make([]protocol.Invoker, 0, len(invokers))
   107  	for _, invoker := range invokers {
   108  		if s.matchThen(invoker.GetURL(), url) {
   109  			result = append(result, invoker)
   110  		}
   111  	}
   112  
   113  	if len(result) != 0 {
   114  		return result
   115  	} else if s.force {
   116  		logger.Warn("execute condition state router result list is empty. and force=true")
   117  		return result
   118  	}
   119  
   120  	return invokers
   121  }
   122  
   123  func (s *StateRouter) URL() *common.URL {
   124  	return s.url
   125  }
   126  
   127  func (s *StateRouter) Priority() int64 {
   128  	return 0
   129  }
   130  
   131  func (s *StateRouter) matchWhen(url *common.URL, invocation protocol.Invocation) bool {
   132  	if len(s.whenCondition) == 0 {
   133  		return true
   134  	}
   135  	return doMatch(url, nil, invocation, s.whenCondition, true)
   136  }
   137  
   138  func (s *StateRouter) matchThen(url *common.URL, param *common.URL) bool {
   139  	if len(s.thenCondition) == 0 {
   140  		return false
   141  	}
   142  	return doMatch(url, param, nil, s.thenCondition, false)
   143  }
   144  
   145  func generateMatcher(url *common.URL) (when, then map[string]matcher.Matcher, err error) {
   146  	rule := url.GetParam(constant.RuleKey, "")
   147  	if rule == "" || len(strings.Trim(rule, " ")) == 0 {
   148  		return nil, nil, errors.Errorf("Illegal route rule!")
   149  	}
   150  	rule = strings.Replace(rule, "consumer.", "", -1)
   151  	rule = strings.Replace(rule, "provider.", "", -1)
   152  	i := strings.Index(rule, "=>")
   153  	// for the case of `{when rule} => {then rule}`
   154  	var whenRule string
   155  	var thenRule string
   156  	if i < 0 {
   157  		whenRule = ""
   158  		thenRule = strings.Trim(rule, " ")
   159  	} else {
   160  		whenRule = strings.Trim(rule[0:i], " ")
   161  		thenRule = strings.Trim(rule[i+2:], " ")
   162  	}
   163  
   164  	when, err = parseWhen(whenRule)
   165  	if err != nil {
   166  		return nil, nil, err
   167  	}
   168  
   169  	then, err = parseThen(thenRule)
   170  	if err != nil {
   171  		return nil, nil, err
   172  	}
   173  	// NOTE: It should be determined on the business level whether the `When condition` can be empty or not.
   174  	return when, then, nil
   175  }
   176  
   177  func parseWhen(whenRule string) (map[string]matcher.Matcher, error) {
   178  	if whenRule == "" || whenRule == " " || whenRule == "true" {
   179  		return make(map[string]matcher.Matcher), nil
   180  	} else {
   181  		when, err := parseRule(whenRule)
   182  		if err != nil {
   183  			return nil, err
   184  		}
   185  		return when, nil
   186  	}
   187  }
   188  
   189  func parseThen(thenRule string) (map[string]matcher.Matcher, error) {
   190  	if thenRule == "" || thenRule == " " || thenRule == "false" {
   191  		return nil, nil
   192  	} else {
   193  		when, err := parseRule(thenRule)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  		return when, nil
   198  	}
   199  }
   200  
   201  func parseRule(rule string) (map[string]matcher.Matcher, error) {
   202  	if isRuleEmpty(rule) {
   203  		return make(map[string]matcher.Matcher), nil
   204  	}
   205  
   206  	condition, err := processMatchers(rule)
   207  	if err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	return condition, nil
   212  }
   213  
   214  func isRuleEmpty(rule string) bool {
   215  	return rule == "" || (len(rule) == 1 && rule[0] == ' ')
   216  }
   217  
   218  func processMatchers(rule string) (map[string]matcher.Matcher, error) {
   219  	condition := make(map[string]matcher.Matcher)
   220  	var currentMatcher matcher.Matcher
   221  	var err error
   222  	values := make(map[string]struct{})
   223  
   224  	for _, matchers := range routePattern.FindAllStringSubmatch(rule, -1) {
   225  		separator := matchers[1]
   226  		content := matchers[2]
   227  
   228  		switch separator {
   229  		case "":
   230  			currentMatcher = getMatcher(content)
   231  			condition[content] = currentMatcher
   232  		case "&":
   233  			currentMatcher, condition = processAndSeparator(content, condition)
   234  		case "=", "!=":
   235  			values, currentMatcher, err = processEqualNotEqualSeparator(separator, content, currentMatcher, rule)
   236  			if err != nil {
   237  				return nil, err
   238  			}
   239  		case ",":
   240  			values, err = processCommaSeparator(content, values, rule)
   241  			if err != nil {
   242  				return nil, err
   243  			}
   244  		default:
   245  			return nil, errors.Errorf(illegalMsg, rule, separator, content)
   246  		}
   247  	}
   248  
   249  	return condition, nil
   250  }
   251  
   252  func processAndSeparator(content string, condition map[string]matcher.Matcher) (matcher.Matcher, map[string]matcher.Matcher) {
   253  	currentMatcher := condition[content]
   254  	if currentMatcher == nil {
   255  		currentMatcher = getMatcher(content)
   256  		condition[content] = currentMatcher
   257  	}
   258  	return currentMatcher, condition
   259  }
   260  
   261  func processEqualNotEqualSeparator(separator, content string, currentMatcher matcher.Matcher, rule string) (map[string]struct{}, matcher.Matcher, error) {
   262  	if currentMatcher == nil {
   263  		return nil, nil, errors.Errorf(illegalMsg, rule, separator, content)
   264  	}
   265  	values := currentMatcher.GetMatches()
   266  	if separator == "!=" {
   267  		values = currentMatcher.GetMismatches()
   268  	}
   269  	values[content] = struct{}{}
   270  	return values, currentMatcher, nil
   271  }
   272  
   273  func processCommaSeparator(content string, values map[string]struct{}, rule string) (map[string]struct{}, error) {
   274  	if len(values) == 0 {
   275  		return nil, errors.Errorf(illegalMsg, rule, ",", content)
   276  	}
   277  	values[content] = struct{}{}
   278  	return values, nil
   279  }
   280  
   281  func getMatcher(key string) matcher.Matcher {
   282  	for _, factory := range matcherFactories {
   283  		if factory.ShouldMatch(key) {
   284  			return factory.NewMatcher(key)
   285  		}
   286  	}
   287  	return matcher.GetMatcherFactory(constant.Param).NewMatcher(key)
   288  }
   289  
   290  func doMatch(url *common.URL, param *common.URL, invocation protocol.Invocation, conditions map[string]matcher.Matcher, isWhenCondition bool) bool {
   291  	sample := url.ToMap()
   292  	for _, matcherPair := range conditions {
   293  		if !matcher.Match(matcherPair, sample, param, invocation, isWhenCondition) {
   294  			return false
   295  		}
   296  	}
   297  	return true
   298  }
   299  
   300  func initMatcherFactories() {
   301  	factoriesMap := matcher.GetMatcherFactories()
   302  	if len(factoriesMap) == 0 {
   303  		return
   304  	}
   305  	for _, factory := range factoriesMap {
   306  		matcherFactories = append(matcherFactories, factory())
   307  	}
   308  	sortMatcherFactories(matcherFactories)
   309  }
   310  
   311  func sortMatcherFactories(matcherFactories []matcher.ConditionMatcherFactory) {
   312  	sort.Stable(byPriority(matcherFactories))
   313  }
   314  
   315  type byPriority []matcher.ConditionMatcherFactory
   316  
   317  func (a byPriority) Len() int           { return len(a) }
   318  func (a byPriority) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   319  func (a byPriority) Less(i, j int) bool { return a[i].Priority() < a[j].Priority() }
   320  
   321  func parseRoute(routeContent string) (*config.RouterConfig, error) {
   322  	routeDecoder := yaml.NewDecoder(strings.NewReader(routeContent))
   323  	routerConfig := &config.RouterConfig{}
   324  	err := routeDecoder.Decode(routerConfig)
   325  	if err != nil {
   326  		return nil, err
   327  	}
   328  	return routerConfig, nil
   329  }