github.com/fufuok/balancer@v1.0.0/wrr.go (about) 1 package balancer 2 3 import ( 4 "sync" 5 6 "github.com/fufuok/balancer/utils" 7 ) 8 9 // Weighted Round-Robin Scheduling 10 // Ref: http://kb.linuxvirtualserver.org/wiki/Weighted_Round-Robin_Scheduling 11 type wrr struct { 12 items []*wrrItem 13 i int 14 n int 15 cw int 16 gcd int 17 max int 18 all map[string]int 19 20 sync.Mutex 21 } 22 23 type wrrItem struct { 24 item string 25 weight int 26 } 27 28 func NewWeightedRoundRobin(items ...map[string]int) (lb *wrr) { 29 if len(items) > 0 && len(items[0]) > 0 { 30 lb = &wrr{} 31 lb.Update(items[0]) 32 return 33 } 34 return &wrr{ 35 all: make(map[string]int), 36 } 37 } 38 39 func (b *wrr) Add(item string, weight ...int) { 40 w := 1 41 if len(weight) > 0 { 42 w = weight[0] 43 } 44 45 b.Lock() 46 b.add(item, w) 47 b.Unlock() 48 } 49 50 func (b *wrr) add(item string, weight int) { 51 b.remove(item) 52 53 b.items = append(b.items, &wrrItem{ 54 item: item, 55 weight: weight, 56 }) 57 b.n++ 58 b.all[item] = weight 59 60 b.addSettings(weight) 61 } 62 63 func (b *wrr) addSettings(weight int) { 64 if weight > 0 { 65 if b.gcd == 0 { 66 b.i = -1 67 b.cw = 0 68 b.gcd = weight 69 b.max = weight 70 } else { 71 b.gcd = utils.GCD(b.gcd, weight) 72 if b.max < weight { 73 b.max = weight 74 } 75 } 76 } 77 } 78 79 func (b *wrr) All() interface{} { 80 all := make(map[string]int) 81 82 b.Lock() 83 for k, v := range b.all { 84 all[k] = v 85 } 86 b.Unlock() 87 88 return all 89 } 90 91 func (b *wrr) Name() string { 92 return "WeightedRoundRobin" 93 } 94 95 func (b *wrr) Select(_ ...string) (item string) { 96 b.Lock() 97 switch b.n { 98 case 0: 99 item = "" 100 case 1: 101 if b.items[0].weight > 0 { 102 item = b.items[0].item 103 } 104 default: 105 item = b.chooseNext().item 106 } 107 b.Unlock() 108 109 return 110 } 111 112 func (b *wrr) chooseNext() *wrrItem { 113 for { 114 b.i = (b.i + 1) % b.n 115 if b.i == 0 { 116 b.cw = b.cw - b.gcd 117 if b.cw <= 0 { 118 b.cw = b.max 119 if b.cw == 0 { 120 return nil 121 } 122 } 123 } 124 125 if b.items[b.i].weight >= b.cw { 126 return b.items[b.i] 127 } 128 } 129 } 130 131 func (b *wrr) Remove(item string, _ ...bool) bool { 132 b.Lock() 133 defer b.Unlock() 134 135 return b.remove(item) 136 } 137 138 func (b *wrr) remove(item string) (ok bool) { 139 maxWeight := 0 140 for i := 0; i < b.n; i++ { 141 if item == b.items[i].item { 142 if b.max == b.items[i].weight { 143 b.max = maxWeight 144 } 145 b.items = append(b.items[:i], b.items[i+1:]...) 146 b.n-- 147 delete(b.all, item) 148 ok = true 149 return 150 } 151 if b.items[i].weight > maxWeight { 152 maxWeight = b.items[i].weight 153 } 154 } 155 return 156 } 157 158 func (b *wrr) RemoveAll() { 159 b.Lock() 160 b.removeAll() 161 b.Unlock() 162 } 163 164 func (b *wrr) removeAll() { 165 b.items = b.items[:0] 166 b.n = 0 167 b.i = -1 168 b.cw = 0 169 b.gcd = 0 170 b.max = 0 171 b.all = make(map[string]int) 172 } 173 174 func (b *wrr) Reset() { 175 b.Lock() 176 b.i = -1 177 b.cw = 0 178 b.Unlock() 179 } 180 181 func (b *wrr) Update(items interface{}) bool { 182 v, ok := items.(map[string]int) 183 if !ok { 184 return false 185 } 186 187 b.Lock() 188 defer b.Unlock() 189 190 b.n = len(v) 191 b.i = -1 192 b.cw = 0 193 b.gcd = 0 194 b.max = 0 195 b.all = v 196 197 if cap(b.items) >= b.n { 198 b.items = b.items[:b.n] 199 } else { 200 b.items = make([]*wrrItem, b.n) 201 } 202 203 i := 0 204 for item, weight := range v { 205 b.items[i] = &wrrItem{ 206 item: item, 207 weight: weight, 208 } 209 b.addSettings(weight) 210 i++ 211 } 212 213 return true 214 }