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  }