150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package gost
|
|
|
|
import (
|
|
"errors"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
// ErrNoneAvailable indicates there is no node available
|
|
ErrNoneAvailable = errors.New("none available")
|
|
)
|
|
|
|
// NodeSelector as a mechanism to pick nodes and mark their status.
|
|
type NodeSelector interface {
|
|
Select(nodes []Node, opts ...SelectOption) (Node, error)
|
|
// Mark(node Node)
|
|
}
|
|
|
|
type defaultSelector struct {
|
|
}
|
|
|
|
func (s *defaultSelector) Select(nodes []Node, opts ...SelectOption) (Node, error) {
|
|
sopts := SelectOptions{
|
|
Strategy: &RoundStrategy{},
|
|
}
|
|
for _, opt := range opts {
|
|
opt(&sopts)
|
|
}
|
|
|
|
for _, filter := range sopts.Filters {
|
|
nodes = filter.Filter(nodes)
|
|
}
|
|
if len(nodes) == 0 {
|
|
return Node{}, ErrNoneAvailable
|
|
}
|
|
return sopts.Strategy.Apply(nodes), nil
|
|
}
|
|
|
|
// Filter is used to filter a node during the selection process
|
|
type Filter interface {
|
|
Filter([]Node) []Node
|
|
}
|
|
|
|
// Strategy is a selection strategy e.g random, round robin
|
|
type Strategy interface {
|
|
Apply([]Node) Node
|
|
String() string
|
|
}
|
|
|
|
// RoundStrategy is a strategy for node selector
|
|
type RoundStrategy struct {
|
|
count uint64
|
|
}
|
|
|
|
// Apply applies the round robin strategy for the nodes
|
|
func (s *RoundStrategy) Apply(nodes []Node) Node {
|
|
if len(nodes) == 0 {
|
|
return Node{}
|
|
}
|
|
old := s.count
|
|
atomic.AddUint64(&s.count, 1)
|
|
return nodes[int(old%uint64(len(nodes)))]
|
|
}
|
|
|
|
func (s *RoundStrategy) String() string {
|
|
return "round"
|
|
}
|
|
|
|
// RandomStrategy is a strategy for node selector
|
|
type RandomStrategy struct{}
|
|
|
|
// Apply applies the random strategy for the nodes
|
|
func (s *RandomStrategy) Apply(nodes []Node) Node {
|
|
if len(nodes) == 0 {
|
|
return Node{}
|
|
}
|
|
|
|
return nodes[time.Now().Nanosecond()%len(nodes)]
|
|
}
|
|
|
|
func (s *RandomStrategy) String() string {
|
|
return "random"
|
|
}
|
|
|
|
// SelectOption used when making a select call
|
|
type SelectOption func(*SelectOptions)
|
|
|
|
// SelectOptions is the options for node selection
|
|
type SelectOptions struct {
|
|
Filters []Filter
|
|
Strategy Strategy
|
|
}
|
|
|
|
// WithFilter adds a filter function to the list of filters
|
|
// used during the Select call.
|
|
func WithFilter(f ...Filter) SelectOption {
|
|
return func(o *SelectOptions) {
|
|
o.Filters = append(o.Filters, f...)
|
|
}
|
|
}
|
|
|
|
// WithStrategy sets the selector strategy
|
|
func WithStrategy(s Strategy) SelectOption {
|
|
return func(o *SelectOptions) {
|
|
o.Strategy = s
|
|
}
|
|
}
|
|
|
|
// IPSelector as a mechanism to pick IPs and mark their status.
|
|
type IPSelector interface {
|
|
Select(ips []string) (string, error)
|
|
String() string
|
|
}
|
|
|
|
// RandomIPSelector is an IP Selector that selects an IP with random strategy.
|
|
type RandomIPSelector struct {
|
|
}
|
|
|
|
// Select selects an IP from ips list.
|
|
func (s *RandomIPSelector) Select(ips []string) (string, error) {
|
|
if len(ips) == 0 {
|
|
return "", nil
|
|
}
|
|
return ips[time.Now().Nanosecond()%len(ips)], nil
|
|
}
|
|
|
|
func (s *RandomIPSelector) String() string {
|
|
return "random"
|
|
}
|
|
|
|
// RoundRobinIPSelector is an IP Selector that selects an IP with round-robin strategy.
|
|
type RoundRobinIPSelector struct {
|
|
count uint64
|
|
}
|
|
|
|
// Select selects an IP from ips list.
|
|
func (s *RoundRobinIPSelector) Select(ips []string) (string, error) {
|
|
if len(ips) == 0 {
|
|
return "", nil
|
|
}
|
|
old := s.count
|
|
atomic.AddUint64(&s.count, 1)
|
|
return ips[int(old%uint64(len(ips)))], nil
|
|
}
|
|
|
|
func (s *RoundRobinIPSelector) String() string {
|
|
return "round"
|
|
}
|