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 }