strategy_random.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package router
  2. import (
  3. "context"
  4. "google.golang.org/protobuf/runtime/protoiface"
  5. core "github.com/v2fly/v2ray-core/v5"
  6. "github.com/v2fly/v2ray-core/v5/app/observatory"
  7. "github.com/v2fly/v2ray-core/v5/common"
  8. "github.com/v2fly/v2ray-core/v5/common/dice"
  9. "github.com/v2fly/v2ray-core/v5/features"
  10. "github.com/v2fly/v2ray-core/v5/features/extension"
  11. )
  12. // RandomStrategy represents a random balancing strategy
  13. type RandomStrategy struct {
  14. ctx context.Context
  15. settings *StrategyRandomConfig
  16. observatory extension.Observatory
  17. }
  18. func (s *RandomStrategy) GetPrincipleTarget(strings []string) []string {
  19. return strings
  20. }
  21. // NewRandomStrategy creates a new RandomStrategy with settings
  22. func NewRandomStrategy(settings *StrategyRandomConfig) *RandomStrategy {
  23. return &RandomStrategy{
  24. settings: settings,
  25. }
  26. }
  27. func (s *RandomStrategy) InjectContext(ctx context.Context) {
  28. if s != nil {
  29. s.ctx = ctx
  30. }
  31. }
  32. func (s *RandomStrategy) PickOutbound(candidates []string) string {
  33. if s != nil && s.settings.AliveOnly {
  34. // candidates are considered alive unless observed otherwise
  35. if s.observatory == nil {
  36. core.RequireFeatures(s.ctx, func(observatory extension.Observatory) error {
  37. s.observatory = observatory
  38. return nil
  39. })
  40. }
  41. if s.observatory != nil {
  42. var observeReport protoiface.MessageV1
  43. var err error
  44. if s.settings.ObserverTag == "" {
  45. observeReport, err = s.observatory.GetObservation(s.ctx)
  46. } else {
  47. observeReport, err = common.Must2(s.observatory.(features.TaggedFeatures).GetFeaturesByTag(s.settings.ObserverTag)).(extension.Observatory).GetObservation(s.ctx)
  48. }
  49. if err == nil {
  50. aliveTags := make([]string, 0)
  51. if result, ok := observeReport.(*observatory.ObservationResult); ok {
  52. status := result.Status
  53. statusMap := make(map[string]*observatory.OutboundStatus)
  54. for _, outboundStatus := range status {
  55. statusMap[outboundStatus.OutboundTag] = outboundStatus
  56. }
  57. for _, candidate := range candidates {
  58. if outboundStatus, found := statusMap[candidate]; found {
  59. if outboundStatus.Alive {
  60. aliveTags = append(aliveTags, candidate)
  61. }
  62. } else {
  63. // unfound candidate is considered alive
  64. aliveTags = append(aliveTags, candidate)
  65. }
  66. }
  67. candidates = aliveTags
  68. }
  69. }
  70. }
  71. }
  72. count := len(candidates)
  73. if count == 0 {
  74. // goes to fallbackTag
  75. return ""
  76. }
  77. return candidates[dice.Roll(count)]
  78. }
  79. func init() {
  80. common.Must(common.RegisterConfig((*StrategyRandomConfig)(nil), nil))
  81. }