multinet/balancer.go
Dmitrii Stepanov e35ad63f2d
[#11] dialer: Use source IPs intead of interfaces
The use of network interfaces does not cover cases where it is necessary
to use network interfaces to access different subnets.

Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
2024-10-09 16:18:58 +03:00

56 lines
1.5 KiB
Go

package multinet
import (
"context"
"errors"
"fmt"
"net"
"sync/atomic"
)
// BalancerType reperents the algorithm which is used to pick source address.
type BalancerType string
const (
// BalancerTypeNoop picks first address for which link is up.
BalancerTypeNoop BalancerType = ""
// BalancerTypeNoop implements simple round-robin between up links.
// It is not fair in case some links are down.
BalancerTypeRoundRobin BalancerType = "roundrobin"
)
var errNoSuitableNodeFound = errors.New("no suitale node found")
type balancer interface {
DialContext(ctx context.Context, s *Subnet, network, address string) (net.Conn, error)
}
type roundRobin struct {
d *dialer
i atomic.Uint32
}
func (r *roundRobin) DialContext(ctx context.Context, s *Subnet, network, address string) (net.Conn, error) {
next := int(r.i.Add(1))
for i := range s.SourceIPs {
ii := s.SourceIPs[(i+next)%len(s.SourceIPs)]
dd := r.d.dialer
dd.LocalAddr = &net.TCPAddr{IP: net.IP(ii.AsSlice())}
return r.d.dialContext(&dd, ctx, network, address)
}
return nil, fmt.Errorf("(*roundRobin).DialContext: %w", errNoSuitableNodeFound)
}
type firstEnabled struct {
d *dialer
}
func (r *firstEnabled) DialContext(ctx context.Context, s *Subnet, network, address string) (net.Conn, error) {
for i := range s.SourceIPs {
ii := s.SourceIPs[i]
dd := r.d.dialer
dd.LocalAddr = &net.TCPAddr{IP: net.IP(ii.AsSlice())}
return r.d.dialContext(&dd, ctx, network, address)
}
return nil, fmt.Errorf("(*firstEnabled).DialContext: %w", errNoSuitableNodeFound)
}