github.com/XiaoMi/Gaea@v1.2.5/backend/balancer.go (about) 1 // Copyright 2016 The kingshard Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"): you may 4 // not use this file except in compliance with the License. You may obtain 5 // a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations 13 // under the License. 14 15 package backend 16 17 import ( 18 "math/rand" 19 "time" 20 21 "github.com/XiaoMi/Gaea/core/errors" 22 ) 23 24 type balancer struct { 25 total int 26 lastIndex int 27 roundRobinQ []int 28 nodeWeights []int 29 } 30 31 // calculate gcd ? 32 func gcd(ary []int) int { 33 var i int 34 min := ary[0] 35 length := len(ary) 36 for i = 0; i < length; i++ { 37 if ary[i] < min { 38 min = ary[i] 39 } 40 } 41 42 for { 43 isCommon := true 44 for i = 0; i < length; i++ { 45 if ary[i]%min != 0 { 46 isCommon = false 47 break 48 } 49 } 50 if isCommon { 51 break 52 } 53 min-- 54 if min < 1 { 55 break 56 } 57 } 58 return min 59 } 60 61 func newBalancer(nodeWeights []int, total int) *balancer { 62 var sum int 63 var s balancer 64 s.total = total 65 s.lastIndex = 0 66 gcd := gcd(nodeWeights) 67 68 for _, weight := range nodeWeights { 69 sum += weight / gcd 70 } 71 72 s.roundRobinQ = make([]int, 0, sum) 73 for index, weight := range nodeWeights { 74 for j := 0; j < weight/gcd; j++ { 75 s.roundRobinQ = append(s.roundRobinQ, index) 76 } 77 } 78 79 //random order 80 if 1 < len(s.nodeWeights) { 81 r := rand.New(rand.NewSource(time.Now().UnixNano())) 82 for i := 0; i < sum; i++ { 83 x := r.Intn(sum) 84 temp := s.roundRobinQ[x] 85 other := sum % (x + 1) 86 s.roundRobinQ[x] = s.roundRobinQ[other] 87 s.roundRobinQ[other] = temp 88 } 89 } 90 return &s 91 } 92 93 func (b *balancer) next() (int, error) { 94 var index int 95 queueLen := len(b.roundRobinQ) 96 if queueLen == 0 { 97 return 0, errors.ErrNoDatabase 98 } 99 if queueLen == 1 { 100 index = b.roundRobinQ[0] 101 return index, nil 102 } 103 104 b.lastIndex = b.lastIndex % queueLen 105 index = b.roundRobinQ[b.lastIndex] 106 if index >= b.total { 107 return 0, errors.ErrNoDatabase 108 } 109 b.lastIndex++ 110 b.lastIndex = b.lastIndex % queueLen 111 return index, nil 112 }