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  }