dubbo.apache.org/dubbo-go/v3@v3.1.1/cluster/loadbalance/interleavedweightedroundrobin/iwrr.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 iwrr 19 20 import ( 21 "math/rand" 22 "sync" 23 24 "dubbo.apache.org/dubbo-go/v3/cluster/loadbalance" 25 "dubbo.apache.org/dubbo-go/v3/protocol" 26 ) 27 28 type iwrrEntry struct { 29 weight int64 30 invoker protocol.Invoker 31 32 next *iwrrEntry 33 } 34 35 type iwrrQueue struct { 36 head *iwrrEntry 37 tail *iwrrEntry 38 } 39 40 func NewIwrrQueue() *iwrrQueue { 41 return &iwrrQueue{} 42 } 43 44 func (item *iwrrQueue) push(entry *iwrrEntry) { 45 entry.next = nil 46 tail := item.tail 47 item.tail = entry 48 if tail == nil { 49 item.head = entry 50 } else { 51 tail.next = entry 52 } 53 } 54 55 func (item *iwrrQueue) pop() *iwrrEntry { 56 head := item.head 57 next := head.next 58 head.next = nil 59 item.head = next 60 if next == nil { 61 item.tail = nil 62 } 63 return head 64 } 65 66 func (item *iwrrQueue) empty() bool { 67 return item.head == nil 68 } 69 70 // InterleavedweightedRoundRobin struct 71 type interleavedweightedRoundRobin struct { 72 current *iwrrQueue 73 next *iwrrQueue 74 step int64 75 mu sync.Mutex 76 } 77 78 func NewInterleavedweightedRoundRobin(invokers []protocol.Invoker, invocation protocol.Invocation) *interleavedweightedRoundRobin { 79 iwrrp := new(interleavedweightedRoundRobin) 80 iwrrp.current = NewIwrrQueue() 81 iwrrp.next = NewIwrrQueue() 82 83 size := uint64(len(invokers)) 84 offset := rand.Uint64() % size 85 step := int64(0) 86 for idx := uint64(0); idx < size; idx++ { 87 invoker := invokers[(idx+offset)%size] 88 weight := loadbalance.GetWeight(invoker, invocation) 89 step = gcdInt(step, weight) 90 iwrrp.current.push(&iwrrEntry{ 91 invoker: invoker, 92 weight: weight, 93 }) 94 } 95 iwrrp.step = step 96 97 return iwrrp 98 } 99 100 func (iwrr *interleavedweightedRoundRobin) Pick(invocation protocol.Invocation) protocol.Invoker { 101 iwrr.mu.Lock() 102 defer iwrr.mu.Unlock() 103 104 if iwrr.current.empty() { 105 iwrr.current, iwrr.next = iwrr.next, iwrr.current 106 } 107 108 entry := iwrr.current.pop() 109 entry.weight -= iwrr.step 110 111 if entry.weight > 0 { 112 iwrr.current.push(entry) 113 } else { 114 weight := loadbalance.GetWeight(entry.invoker, invocation) 115 if weight < 0 { 116 weight = 0 117 } 118 entry.weight = weight 119 iwrr.next.push(entry) 120 } 121 122 return entry.invoker 123 } 124 125 func gcdInt(a, b int64) int64 { 126 for b != 0 { 127 a, b = b, a%b 128 } 129 return a 130 }