dubbo.apache.org/dubbo-go/v3@v3.1.1/cluster/router/condition/dynamic_router.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  	"strconv"
    22  	"strings"
    23  	"sync"
    24  )
    25  
    26  import (
    27  	"github.com/dubbogo/gost/log/logger"
    28  )
    29  
    30  import (
    31  	"dubbo.apache.org/dubbo-go/v3/common"
    32  	conf "dubbo.apache.org/dubbo-go/v3/common/config"
    33  	"dubbo.apache.org/dubbo-go/v3/common/constant"
    34  	"dubbo.apache.org/dubbo-go/v3/config"
    35  	"dubbo.apache.org/dubbo-go/v3/config_center"
    36  	"dubbo.apache.org/dubbo-go/v3/protocol"
    37  	"dubbo.apache.org/dubbo-go/v3/remoting"
    38  )
    39  
    40  type DynamicRouter struct {
    41  	conditionRouters []*StateRouter
    42  	routerConfig     *config.RouterConfig
    43  }
    44  
    45  func (d *DynamicRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker {
    46  	if len(invokers) == 0 || len(d.conditionRouters) == 0 {
    47  		return invokers
    48  	}
    49  
    50  	for _, router := range d.conditionRouters {
    51  		invokers = router.Route(invokers, url, invocation)
    52  	}
    53  	return invokers
    54  }
    55  
    56  func (d *DynamicRouter) URL() *common.URL {
    57  	return nil
    58  }
    59  
    60  func (d *DynamicRouter) Process(event *config_center.ConfigChangeEvent) {
    61  	if event.ConfigType == remoting.EventTypeDel {
    62  		d.routerConfig = nil
    63  		d.conditionRouters = make([]*StateRouter, 0)
    64  	} else {
    65  		routerConfig, err := parseRoute(event.Value.(string))
    66  		if err != nil {
    67  			logger.Warnf("[condition router]Build a new condition route config error, %+v and we will use the original condition rule configuration.", err)
    68  			return
    69  		}
    70  		d.routerConfig = routerConfig
    71  		conditions, err := generateConditions(d.routerConfig)
    72  		if err != nil {
    73  			logger.Warnf("[condition router]Build a new condition route config error, %+v and we will use the original condition rule configuration.", err)
    74  			return
    75  		}
    76  		d.conditionRouters = conditions
    77  	}
    78  }
    79  
    80  func generateConditions(routerConfig *config.RouterConfig) ([]*StateRouter, error) {
    81  	if routerConfig == nil {
    82  		return make([]*StateRouter, 0), nil
    83  	}
    84  	conditionRouters := make([]*StateRouter, 0, len(routerConfig.Conditions))
    85  	for _, conditionRule := range routerConfig.Conditions {
    86  		url, err := common.NewURL("condition://")
    87  		if err != nil {
    88  			return nil, err
    89  		}
    90  		url.AddParam(constant.RuleKey, conditionRule)
    91  		url.AddParam(constant.ForceKey, strconv.FormatBool(*routerConfig.Force))
    92  		url.AddParam(constant.EnabledKey, strconv.FormatBool(*routerConfig.Enabled))
    93  		conditionRoute, err := NewConditionStateRouter(url)
    94  		if err != nil {
    95  			return nil, err
    96  		}
    97  		conditionRouters = append(conditionRouters, conditionRoute)
    98  	}
    99  	return conditionRouters, nil
   100  }
   101  
   102  // ServiceRouter is Service level router
   103  type ServiceRouter struct {
   104  	DynamicRouter
   105  }
   106  
   107  func NewServiceRouter() *ServiceRouter {
   108  	return &ServiceRouter{}
   109  }
   110  
   111  func (s *ServiceRouter) Priority() int64 {
   112  	return 140
   113  }
   114  
   115  func (s *ServiceRouter) Notify(invokers []protocol.Invoker) {
   116  	if len(invokers) == 0 {
   117  		return
   118  	}
   119  	url := invokers[0].GetURL()
   120  	if url == nil {
   121  		logger.Error("Failed to notify a dynamically condition rule, because url is empty")
   122  		return
   123  	}
   124  
   125  	dynamicConfiguration := conf.GetEnvInstance().GetDynamicConfiguration()
   126  	if dynamicConfiguration == nil {
   127  		logger.Infof("Config center does not start, Condition router will not be enabled")
   128  		return
   129  	}
   130  	key := strings.Join([]string{url.ColonSeparatedKey(), constant.ConditionRouterRuleSuffix}, "")
   131  	dynamicConfiguration.AddListener(key, s)
   132  	value, err := dynamicConfiguration.GetRule(key)
   133  	if err != nil {
   134  		logger.Errorf("Failed to query condition rule, key=%s, err=%v", key, err)
   135  		return
   136  	}
   137  	s.Process(&config_center.ConfigChangeEvent{Key: key, Value: value, ConfigType: remoting.EventTypeAdd})
   138  }
   139  
   140  // ApplicationRouter is Application level router
   141  type ApplicationRouter struct {
   142  	DynamicRouter
   143  	application        string
   144  	currentApplication string
   145  	mu                 sync.Mutex
   146  }
   147  
   148  func NewApplicationRouter() *ApplicationRouter {
   149  	applicationName := config.GetApplicationConfig().Name
   150  	a := &ApplicationRouter{
   151  		currentApplication: applicationName,
   152  	}
   153  
   154  	dynamicConfiguration := conf.GetEnvInstance().GetDynamicConfiguration()
   155  	if dynamicConfiguration != nil {
   156  		dynamicConfiguration.AddListener(strings.Join([]string{applicationName, constant.ConditionRouterRuleSuffix}, ""), a)
   157  	}
   158  	return a
   159  }
   160  
   161  func (a *ApplicationRouter) Priority() int64 {
   162  	return 145
   163  }
   164  
   165  func (a *ApplicationRouter) Notify(invokers []protocol.Invoker) {
   166  	if len(invokers) == 0 {
   167  		return
   168  	}
   169  	url := invokers[0].GetURL()
   170  	if url == nil {
   171  		logger.Error("Failed to notify a dynamically condition rule, because url is empty")
   172  		return
   173  	}
   174  	providerApplicaton := url.GetParam("application", "")
   175  	if providerApplicaton == "" || providerApplicaton == a.currentApplication {
   176  		logger.Warn("condition router get providerApplication is empty, will not subscribe to provider app rules.")
   177  		return
   178  	}
   179  
   180  	a.mu.Lock()
   181  	defer a.mu.Unlock()
   182  
   183  	if providerApplicaton != a.application {
   184  		dynamicConfiguration := conf.GetEnvInstance().GetDynamicConfiguration()
   185  		if dynamicConfiguration == nil {
   186  			logger.Infof("Config center does not start, Condition router will not be enabled")
   187  			return
   188  		}
   189  
   190  		if a.application != "" {
   191  			dynamicConfiguration.RemoveListener(strings.Join([]string{a.application, constant.ConditionRouterRuleSuffix}, ""), a)
   192  		}
   193  
   194  		key := strings.Join([]string{providerApplicaton, constant.ConditionRouterRuleSuffix}, "")
   195  		dynamicConfiguration.AddListener(key, a)
   196  		a.application = providerApplicaton
   197  		value, err := dynamicConfiguration.GetRule(key)
   198  		if err != nil {
   199  			logger.Errorf("Failed to query condition rule, key=%s, err=%v", key, err)
   200  			return
   201  		}
   202  		a.Process(&config_center.ConfigChangeEvent{Key: key, Value: value, ConfigType: remoting.EventTypeUpdate})
   203  	}
   204  }