github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/app/observatory/burst/burstobserver.go (about)

     1  package burst
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  
     7  	"github.com/golang/protobuf/proto"
     8  
     9  	core "github.com/v2fly/v2ray-core/v5"
    10  	"github.com/v2fly/v2ray-core/v5/app/observatory"
    11  	"github.com/v2fly/v2ray-core/v5/common"
    12  	"github.com/v2fly/v2ray-core/v5/common/signal/done"
    13  	"github.com/v2fly/v2ray-core/v5/features/extension"
    14  	"github.com/v2fly/v2ray-core/v5/features/outbound"
    15  )
    16  
    17  type Observer struct {
    18  	config *Config
    19  	ctx    context.Context
    20  
    21  	statusLock sync.Mutex // nolint: structcheck
    22  	hp         *HealthPing
    23  
    24  	finished *done.Instance
    25  
    26  	ohm outbound.Manager
    27  }
    28  
    29  func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
    30  	return &observatory.ObservationResult{Status: o.createResult()}, nil
    31  }
    32  
    33  func (o *Observer) createResult() []*observatory.OutboundStatus {
    34  	var result []*observatory.OutboundStatus
    35  	o.hp.access.Lock()
    36  	defer o.hp.access.Unlock()
    37  	for name, value := range o.hp.Results {
    38  		status := observatory.OutboundStatus{
    39  			Alive:           value.getStatistics().All != value.getStatistics().Fail,
    40  			Delay:           value.getStatistics().Average.Milliseconds(),
    41  			LastErrorReason: "",
    42  			OutboundTag:     name,
    43  			LastSeenTime:    0,
    44  			LastTryTime:     0,
    45  			HealthPing: &observatory.HealthPingMeasurementResult{
    46  				All:       int64(value.getStatistics().All),
    47  				Fail:      int64(value.getStatistics().Fail),
    48  				Deviation: int64(value.getStatistics().Deviation),
    49  				Average:   int64(value.getStatistics().Average),
    50  				Max:       int64(value.getStatistics().Max),
    51  				Min:       int64(value.getStatistics().Min),
    52  			},
    53  		}
    54  		result = append(result, &status)
    55  	}
    56  	return result
    57  }
    58  
    59  func (o *Observer) Type() interface{} {
    60  	return extension.ObservatoryType()
    61  }
    62  
    63  func (o *Observer) Start() error {
    64  	if o.config != nil && len(o.config.SubjectSelector) != 0 {
    65  		o.finished = done.New()
    66  		o.hp.StartScheduler(func() ([]string, error) {
    67  			hs, ok := o.ohm.(outbound.HandlerSelector)
    68  			if !ok {
    69  				return nil, newError("outbound.Manager is not a HandlerSelector")
    70  			}
    71  
    72  			outbounds := hs.Select(o.config.SubjectSelector)
    73  			return outbounds, nil
    74  		})
    75  	}
    76  	return nil
    77  }
    78  
    79  func (o *Observer) Close() error {
    80  	if o.finished != nil {
    81  		o.hp.StopScheduler()
    82  		return o.finished.Close()
    83  	}
    84  	return nil
    85  }
    86  
    87  func New(ctx context.Context, config *Config) (*Observer, error) {
    88  	var outboundManager outbound.Manager
    89  	err := core.RequireFeatures(ctx, func(om outbound.Manager) {
    90  		outboundManager = om
    91  	})
    92  	if err != nil {
    93  		return nil, newError("Cannot get depended features").Base(err)
    94  	}
    95  	hp := NewHealthPing(ctx, config.PingConfig)
    96  	return &Observer{
    97  		config: config,
    98  		ctx:    ctx,
    99  		ohm:    outboundManager,
   100  		hp:     hp,
   101  	}, nil
   102  }
   103  
   104  func init() {
   105  	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
   106  		return New(ctx, config.(*Config))
   107  	}))
   108  }