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  }