dubbo.apache.org/dubbo-go/v3@v3.1.1/cluster/loadbalance/p2c/loadbalance.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 p2c 19 20 import ( 21 "errors" 22 "fmt" 23 "math/rand" 24 "sync" 25 "time" 26 ) 27 28 import ( 29 "github.com/dubbogo/gost/log/logger" 30 ) 31 32 import ( 33 "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance" 34 "dubbo.apache.org/dubbo-go/v3/cluster/metrics" 35 "dubbo.apache.org/dubbo-go/v3/common/constant" 36 "dubbo.apache.org/dubbo-go/v3/common/extension" 37 "dubbo.apache.org/dubbo-go/v3/protocol" 38 ) 39 40 var ( 41 randSeed = func() int64 { 42 return time.Now().Unix() 43 } 44 ) 45 46 func init() { 47 rand.Seed(randSeed()) 48 extension.SetLoadbalance(constant.LoadBalanceKeyP2C, newP2CLoadBalance) 49 } 50 51 var ( 52 once sync.Once 53 instance loadbalance.LoadBalance 54 ) 55 56 type p2cLoadBalance struct{} 57 58 func newP2CLoadBalance() loadbalance.LoadBalance { 59 if instance == nil { 60 once.Do(func() { 61 instance = &p2cLoadBalance{} 62 }) 63 } 64 return instance 65 } 66 67 func (l *p2cLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker { 68 if len(invokers) == 0 { 69 return nil 70 } 71 if len(invokers) == 1 { 72 return invokers[0] 73 } 74 // m is the Metrics, which saves the metrics of instance, invokers and methods 75 // The local metrics is available only for the earlier version. 76 m := metrics.LocalMetrics 77 // picks two nodes randomly 78 var i, j int 79 if len(invokers) == 2 { 80 i, j = 0, 1 81 } else { 82 i = rand.Intn(len(invokers)) 83 j = i 84 for i == j { 85 j = rand.Intn(len(invokers)) 86 } 87 } 88 logger.Debugf("[P2C select] Two invokers were selected, invoker[%d]: %s, invoker[%d]: %s.", 89 i, invokers[i], j, invokers[j]) 90 91 methodName := invocation.ActualMethodName() 92 // remainingIIface, remainingJIface means remaining capacity of node i and node j. 93 // If one of the metrics is empty, invoke the invocation to that node directly. 94 remainingIIface, err := m.GetMethodMetrics(invokers[i].GetURL(), methodName, metrics.HillClimbing) 95 if err != nil { 96 if errors.Is(err, metrics.ErrMetricsNotFound) { 97 logger.Debugf("[P2C select] The invoker[%d] was selected, because it hasn't been selected before.", i) 98 return invokers[i] 99 } 100 logger.Warnf("get method metrics err: %v", err) 101 return nil 102 } 103 104 // TODO(justxuewei): It should have a strategy to drop some metrics after a period of time. 105 remainingJIface, err := m.GetMethodMetrics(invokers[j].GetURL(), methodName, metrics.HillClimbing) 106 if err != nil { 107 if errors.Is(err, metrics.ErrMetricsNotFound) { 108 logger.Debugf("[P2C select] The invoker[%d] was selected, because it hasn't been selected before.", j) 109 return invokers[j] 110 } 111 logger.Warnf("get method metrics err: %v", err) 112 return nil 113 } 114 115 // Convert interface to int, if the type is unexpected, panic immediately 116 remainingI, ok := remainingIIface.(uint64) 117 if !ok { 118 panic(fmt.Sprintf("[P2C select] the type of %s expects to be uint64, but gets %T", 119 metrics.HillClimbing, remainingIIface)) 120 } 121 122 remainingJ, ok := remainingJIface.(uint64) 123 if !ok { 124 panic(fmt.Sprintf("the type of %s expects to be uint64, but gets %T", metrics.HillClimbing, remainingJIface)) 125 } 126 127 logger.Debugf("[P2C select] The invoker[%d] remaining is %d, and the invoker[%d] is %d.", i, remainingI, j, remainingJ) 128 129 // For the remaining capacity, the bigger, the better. 130 if remainingI > remainingJ { 131 logger.Debugf("[P2C select] The invoker[%d] was selected.", i) 132 return invokers[i] 133 } 134 135 logger.Debugf("[P2C select] The invoker[%d] was selected.", j) 136 return invokers[j] 137 }