diff --git a/adapters/outbound/base.go b/adapters/outbound/base.go index 7941c2f7..c73a1542 100644 --- a/adapters/outbound/base.go +++ b/adapters/outbound/base.go @@ -43,7 +43,9 @@ func (p *Proxy) Alive() bool { func (p *Proxy) Dial(metadata *C.Metadata) (net.Conn, error) { conn, err := p.ProxyAdapter.Dial(metadata) - p.alive = err == nil + if err != nil { + p.alive = false + } return conn, err } @@ -89,6 +91,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { // URLTest get the delay for the specified URL func (p *Proxy) URLTest(url string) (t uint16, err error) { defer func() { + p.alive = err == nil record := C.DelayHistory{Time: time.Now()} if err == nil { record.Delay = t diff --git a/adapters/outbound/loadbalance.go b/adapters/outbound/loadbalance.go index 9c183ccf..df2145b2 100644 --- a/adapters/outbound/loadbalance.go +++ b/adapters/outbound/loadbalance.go @@ -4,6 +4,8 @@ import ( "encoding/json" "errors" "net" + "sync" + "time" "github.com/Dreamacro/clash/common/murmur3" C "github.com/Dreamacro/clash/constant" @@ -15,6 +17,9 @@ type LoadBalance struct { *Base proxies []C.Proxy maxRetry int + rawURL string + interval time.Duration + done chan struct{} } func getKey(metadata *C.Metadata) string { @@ -62,6 +67,38 @@ func (lb *LoadBalance) Dial(metadata *C.Metadata) (net.Conn, error) { return lb.proxies[0].Dial(metadata) } +func (lb *LoadBalance) Destroy() { + lb.done <- struct{}{} +} + +func (lb *LoadBalance) validTest() { + wg := sync.WaitGroup{} + wg.Add(len(lb.proxies)) + + for _, p := range lb.proxies { + go func(p C.Proxy) { + p.URLTest(lb.rawURL) + wg.Done() + }(p) + } + + wg.Wait() +} + +func (lb *LoadBalance) loop() { + tick := time.NewTicker(lb.interval) + go lb.validTest() +Loop: + for { + select { + case <-tick.C: + go lb.validTest() + case <-lb.done: + break Loop + } + } +} + func (lb *LoadBalance) MarshalJSON() ([]byte, error) { var all []string for _, proxy := range lb.proxies { @@ -74,21 +111,30 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) { } type LoadBalanceOption struct { - Name string `proxy:"name"` - Proxies []string `proxy:"proxies"` + Name string `proxy:"name"` + Proxies []string `proxy:"proxies"` + URL string `proxy:"url"` + Interval int `proxy:"interval"` } -func NewLoadBalance(name string, proxies []C.Proxy) (*LoadBalance, error) { +func NewLoadBalance(option LoadBalanceOption, proxies []C.Proxy) (*LoadBalance, error) { if len(proxies) == 0 { return nil, errors.New("Provide at least one proxy") } - return &LoadBalance{ + interval := time.Duration(option.Interval) * time.Second + + lb := &LoadBalance{ Base: &Base{ - name: name, + name: option.Name, tp: C.LoadBalance, }, proxies: proxies, maxRetry: 3, - }, nil + rawURL: option.URL, + interval: interval, + done: make(chan struct{}), + } + go lb.loop() + return lb, nil } diff --git a/config/config.go b/config/config.go index 1c948952..cc769903 100644 --- a/config/config.go +++ b/config/config.go @@ -302,7 +302,7 @@ func parseProxies(cfg *rawConfig) (map[string]C.Proxy, error) { if err != nil { return nil, fmt.Errorf("ProxyGroup %s: %s", groupName, err.Error()) } - group, err = adapters.NewLoadBalance(loadBalanceOption.Name, ps) + group, err = adapters.NewLoadBalance(*loadBalanceOption, ps) } if err != nil { return nil, fmt.Errorf("Proxy %s: %s", groupName, err.Error())