telebit/relay/api/loadbalance.go

111 lines
3.0 KiB
Go

package api
import (
"fmt"
"log"
"sync"
)
type LoadBalanceStrategy string
const (
UnSupported LoadBalanceStrategy = "unsuported"
RoundRobin LoadBalanceStrategy = "round-robin"
LeastConnections LoadBalanceStrategy = "least-connections"
)
//DomainLoadBalance -- Use as a structure for domain connections
//and load balancing those connections. Initial modes are round-robin
//but suspect we will need least-connections, and sticky
type DomainLoadBalance struct {
mutex sync.Mutex
//lb method, supported round robin.
method LoadBalanceStrategy
//the last connection based on calculation
lastmember int
// a list of connections in this load balancing context
connections []*Connection
//a counter to track total connections, so we aren't calling len all the time
count int
//true if the system belives a recalcuation is required
recalc bool
}
//NewDomainLoadBalance -- Constructor
func NewDomainLoadBalance(defaultMethod LoadBalanceStrategy) (p *DomainLoadBalance) {
p = new(DomainLoadBalance)
p.method = defaultMethod
p.lastmember = 0
p.count = 0
return
}
//Connections -- Access connections
func (p *DomainLoadBalance) Connections() []*Connection {
return p.connections
}
//NextMember -- increments the lastmember, and then checks if >= to count, if true
//the last is reset to 0
func (p *DomainLoadBalance) NextMember() (conn *Connection) {
p.mutex.Lock()
defer p.mutex.Unlock()
//check for round robin, if not RR then drop out and call calculate
log.Println("NextMember:", p)
if p.method == RoundRobin {
p.lastmember++
if p.lastmember >= p.count {
p.lastmember = 0
}
nextConn := p.connections[p.lastmember]
return nextConn
}
// Not round robin
switch method := p.method; method {
default:
panic(fmt.Errorf("fatal unsupported loadbalance method %s", method))
}
}
//AddConnection -- Add an additional connection to the list of connections for this domain
//this should not affect the next member calculation in RR. However it many in other
//methods
func (p *DomainLoadBalance) AddConnection(conn *Connection) []*Connection {
log.Println("AddConnection", fmt.Sprintf("%p", conn))
p.mutex.Lock()
defer p.mutex.Unlock()
p.connections = append(p.connections, conn)
p.count++
log.Println("AddConnection", p)
return p.connections
}
//RemoveConnection -- removes a matching connection from the list. This may
//affect the nextmember calculation if found so the recalc flag is set.
func (p *DomainLoadBalance) RemoveConnection(conn *Connection) {
log.Println("RemoveConnection", fmt.Sprintf("%p", conn))
p.mutex.Lock()
defer p.mutex.Unlock()
//scan all the connections
for pos := range p.connections {
log.Println("RemoveConnection", pos, len(p.connections), p.count)
if p.connections[pos] == conn {
//found connection remove it
log.Printf("found connection %p", conn)
p.connections[pos], p.connections = p.connections[len(p.connections)-1], p.connections[:len(p.connections)-1]
p.count--
break
}
}
log.Println("RemoveConnection:", p)
}