diff --git a/balancer/balancer.go b/balancer/balancer.go index dff1e3c7..a8624988 100644 --- a/balancer/balancer.go +++ b/balancer/balancer.go @@ -14,7 +14,7 @@ var _ Balancer = (*rttBasedBalancer)(nil) // Balancer is interface for load balancers type Balancer interface { - Select() *Node + Pick() *Node } type rttBasedBalancer struct { @@ -49,7 +49,7 @@ func newRTTBasedBalancer( } // Select selects qualified nodes -func (s *rttBasedBalancer) Select() *Node { +func (s *rttBasedBalancer) Pick() *Node { nodes := s.HealthCheck.NodesByCategory() var candidates []*Node if len(nodes.Qualified) > 0 { @@ -62,7 +62,7 @@ func (s *rttBasedBalancer) Select() *Node { candidates = nodes.Untested shuffleNodes(candidates) } - selects := pickNodes( + selects := selectNodes( candidates, s.logger, int(s.options.Pick.Expected), s.options.Pick.Baselines, ) @@ -73,62 +73,6 @@ func (s *rttBasedBalancer) Select() *Node { return selects[rand.Intn(count)] } -// pickNodes selects nodes according to Baselines and Expected Count. -// -// The strategy always improves network response speed, not matter which mode below is configurated. -// But they can still have different priorities. -// -// 1. Bandwidth priority: no Baseline + Expected Count > 0.: selects `Expected Count` of nodes. -// (one if Expected Count <= 0) -// -// 2. Bandwidth priority advanced: Baselines + Expected Count > 0. -// Select `Expected Count` amount of nodes, and also those near them according to baselines. -// In other words, it selects according to different Baselines, until one of them matches -// the Expected Count, if no Baseline matches, Expected Count applied. -// -// 3. Speed priority: Baselines + `Expected Count <= 0`. -// go through all baselines until find selects, if not, select none. Used in combination -// with 'balancer.fallbackTag', it means: selects qualified nodes or use the fallback. -func pickNodes(nodes []*Node, logger log.Logger, expected int, baselines []option.Duration) []*Node { - if len(nodes) == 0 { - // s.logger.Debug("no qualified nodes") - return nil - } - expected2 := int(expected) - availableCount := len(nodes) - if expected2 > availableCount { - return nodes - } - - if expected2 <= 0 { - expected2 = 1 - } - if len(baselines) == 0 { - return nodes[:expected2] - } - - count := 0 - // go through all base line until find expected selects - for _, b := range baselines { - baseline := time.Duration(b) - for i := 0; i < availableCount; i++ { - if nodes[i].Weighted > baseline { - break - } - count = i + 1 - } - // don't continue if find expected selects - if count >= expected2 { - logger.Debug("applied baseline: ", baseline) - break - } - } - if expected > 0 && count < expected2 { - count = expected2 - } - return nodes[:count] -} - func sortNodes(nodes []*Node) { sort.Slice(nodes, func(i, j int) bool { left := nodes[i] diff --git a/balancer/balancer_select.go b/balancer/balancer_select.go new file mode 100644 index 00000000..9ae8bd40 --- /dev/null +++ b/balancer/balancer_select.go @@ -0,0 +1,66 @@ +package balancer + +import ( + "time" + + "github.com/sagernet/sing-box/log" + "github.com/sagernet/sing-box/option" +) + +// selectNodes selects nodes according to Baselines and Expected Count. +// +// The strategy always improves network response speed, not matter which mode below is configurated. +// But they can still have different priorities. +// +// 1. Bandwidth priority: no Baseline + Expected Count > 0.: selects `Expected Count` of nodes. +// (one if Expected Count <= 0) +// +// 2. Bandwidth priority advanced: Baselines + Expected Count > 0. +// Select `Expected Count` amount of nodes, and also those near them according to baselines. +// In other words, it selects according to different Baselines, until one of them matches +// the Expected Count, if no Baseline matches, Expected Count applied. +// +// 3. Speed priority: Baselines + `Expected Count <= 0`. +// go through all baselines until find selects, if not, select none. Used in combination +// with 'balancer.fallbackTag', it means: selects qualified nodes or use the fallback. +func selectNodes(nodes []*Node, logger log.Logger, expected int, baselines []option.Duration) []*Node { + if len(nodes) == 0 { + // s.logger.Debug("no qualified nodes") + return nil + } + expected2 := int(expected) + availableCount := len(nodes) + if expected2 > availableCount { + return nodes + } + + if expected2 <= 0 { + expected2 = 1 + } + if len(baselines) == 0 { + return nodes[:expected2] + } + + count := 0 + // go through all base line until find expected selects + for _, b := range baselines { + baseline := time.Duration(b) + for i := 0; i < availableCount; i++ { + if nodes[i].Weighted > baseline { + break + } + count = i + 1 + } + // don't continue if find expected selects + if count >= expected2 { + if logger != nil { + logger.Debug("applied baseline: ", baseline) + } + break + } + } + if expected > 0 && count < expected2 { + count = expected2 + } + return nodes[:count] +} diff --git a/outbound/balancer.go b/outbound/balancer.go index 19ee42d6..eb7574e9 100644 --- a/outbound/balancer.go +++ b/outbound/balancer.go @@ -129,7 +129,7 @@ func (s *Balancer) setBalancer(b balancer.Balancer) error { func (s *Balancer) pick() adapter.Outbound { if s.Balancer != nil { - selected := s.Balancer.Select() + selected := s.Balancer.Pick() if selected == nil { return s.fallback }