balancing.go 1.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. package router
  2. import (
  3. "context"
  4. sync "sync"
  5. "github.com/xtls/xray-core/common/dice"
  6. "github.com/xtls/xray-core/features/extension"
  7. "github.com/xtls/xray-core/features/outbound"
  8. )
  9. type BalancingStrategy interface {
  10. PickOutbound([]string) string
  11. }
  12. type RandomStrategy struct{}
  13. func (s *RandomStrategy) PickOutbound(tags []string) string {
  14. n := len(tags)
  15. if n == 0 {
  16. panic("0 tags")
  17. }
  18. return tags[dice.Roll(n)]
  19. }
  20. type RoundRobinStrategy struct {
  21. mu sync.Mutex
  22. index int
  23. }
  24. func (s *RoundRobinStrategy) PickOutbound(tags []string) string {
  25. n := len(tags)
  26. if n == 0 {
  27. panic("0 tags")
  28. }
  29. s.mu.Lock()
  30. defer s.mu.Unlock()
  31. tag := tags[s.index%n]
  32. s.index = (s.index + 1) % n
  33. return tag
  34. }
  35. type Balancer struct {
  36. selectors []string
  37. strategy BalancingStrategy
  38. ohm outbound.Manager
  39. }
  40. func (b *Balancer) PickOutbound() (string, error) {
  41. hs, ok := b.ohm.(outbound.HandlerSelector)
  42. if !ok {
  43. return "", newError("outbound.Manager is not a HandlerSelector")
  44. }
  45. tags := hs.Select(b.selectors)
  46. if len(tags) == 0 {
  47. return "", newError("no available outbounds selected")
  48. }
  49. tag := b.strategy.PickOutbound(tags)
  50. if tag == "" {
  51. return "", newError("balancing strategy returns empty tag")
  52. }
  53. return tag, nil
  54. }
  55. func (b *Balancer) InjectContext(ctx context.Context) {
  56. if contextReceiver, ok := b.strategy.(extension.ContextReceiver); ok {
  57. contextReceiver.InjectContext(ctx)
  58. }
  59. }