github.com/igoogolx/clash@v1.19.8/adapter/provider/healthcheck.go (about)

     1  package provider
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/igoogolx/clash/common/batch"
     8  	C "github.com/igoogolx/clash/constant"
     9  
    10  	"github.com/samber/lo"
    11  	"go.uber.org/atomic"
    12  )
    13  
    14  const (
    15  	defaultURLTestTimeout = time.Second * 5
    16  )
    17  
    18  type HealthCheckOption struct {
    19  	URL      string
    20  	Interval uint
    21  }
    22  
    23  type HealthCheck struct {
    24  	url       string
    25  	proxies   []C.Proxy
    26  	interval  uint
    27  	lazy      bool
    28  	lastTouch *atomic.Int64
    29  	done      chan struct{}
    30  }
    31  
    32  func (hc *HealthCheck) process() {
    33  	ticker := time.NewTicker(time.Duration(hc.interval) * time.Second)
    34  
    35  	go hc.checkAll()
    36  	for {
    37  		select {
    38  		case <-ticker.C:
    39  			now := time.Now().Unix()
    40  			if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) {
    41  				hc.checkAll()
    42  			} else { // lazy but still need to check not alive proxies
    43  				notAliveProxies := lo.Filter(hc.proxies, func(proxy C.Proxy, _ int) bool {
    44  					return !proxy.Alive()
    45  				})
    46  				if len(notAliveProxies) != 0 {
    47  					hc.check(notAliveProxies)
    48  				}
    49  			}
    50  		case <-hc.done:
    51  			ticker.Stop()
    52  			return
    53  		}
    54  	}
    55  }
    56  
    57  func (hc *HealthCheck) setProxy(proxies []C.Proxy) {
    58  	hc.proxies = proxies
    59  }
    60  
    61  func (hc *HealthCheck) auto() bool {
    62  	return hc.interval != 0
    63  }
    64  
    65  func (hc *HealthCheck) touch() {
    66  	hc.lastTouch.Store(time.Now().Unix())
    67  }
    68  
    69  func (hc *HealthCheck) checkAll() {
    70  	hc.check(hc.proxies)
    71  }
    72  
    73  func (hc *HealthCheck) check(proxies []C.Proxy) {
    74  	b, _ := batch.New(context.Background(), batch.WithConcurrencyNum(10))
    75  	for _, proxy := range proxies {
    76  		p := proxy
    77  		b.Go(p.Name(), func() (any, error) {
    78  			ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
    79  			defer cancel()
    80  			p.URLTest(ctx, hc.url)
    81  			return nil, nil
    82  		})
    83  	}
    84  	b.Wait()
    85  }
    86  
    87  func (hc *HealthCheck) close() {
    88  	hc.done <- struct{}{}
    89  }
    90  
    91  func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *HealthCheck {
    92  	return &HealthCheck{
    93  		proxies:   proxies,
    94  		url:       url,
    95  		interval:  interval,
    96  		lazy:      lazy,
    97  		lastTouch: atomic.NewInt64(0),
    98  		done:      make(chan struct{}, 1),
    99  	}
   100  }