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