Skip to content

Commit

Permalink
Feature: add lazy for proxy group and provider
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamacro committed Nov 18, 2020
1 parent 4735f61 commit 0402878
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 59 deletions.
8 changes: 6 additions & 2 deletions adapters/outboundgroup/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ const (
defaultGetProxiesDuration = time.Second * 5
)

func getProvidersProxies(providers []provider.ProxyProvider) []C.Proxy {
func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy {
proxies := []C.Proxy{}
for _, provider := range providers {
proxies = append(proxies, provider.Proxies()...)
if touch {
proxies = append(proxies, provider.ProxiesWithTouch()...)
} else {
proxies = append(proxies, provider.Proxies()...)
}
}
return proxies
}
22 changes: 11 additions & 11 deletions adapters/outboundgroup/fallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ type Fallback struct {
}

func (f *Fallback) Now() string {
proxy := f.findAliveProxy()
proxy := f.findAliveProxy(false)
return proxy.Name()
}

func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
proxy := f.findAliveProxy()
proxy := f.findAliveProxy(true)
c, err := proxy.DialContext(ctx, metadata)
if err == nil {
c.AppendToChains(f)
Expand All @@ -32,7 +32,7 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata) (C.Con
}

func (f *Fallback) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
proxy := f.findAliveProxy()
proxy := f.findAliveProxy(true)
pc, err := proxy.DialUDP(metadata)
if err == nil {
pc.AppendToChains(f)
Expand All @@ -45,13 +45,13 @@ func (f *Fallback) SupportUDP() bool {
return false
}

proxy := f.findAliveProxy()
proxy := f.findAliveProxy(false)
return proxy.SupportUDP()
}

func (f *Fallback) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range f.proxies() {
for _, proxy := range f.proxies(false) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]interface{}{
Expand All @@ -62,27 +62,27 @@ func (f *Fallback) MarshalJSON() ([]byte, error) {
}

func (f *Fallback) Unwrap(metadata *C.Metadata) C.Proxy {
proxy := f.findAliveProxy()
proxy := f.findAliveProxy(true)
return proxy
}

func (f *Fallback) proxies() []C.Proxy {
func (f *Fallback) proxies(touch bool) []C.Proxy {
elm, _, _ := f.single.Do(func() (interface{}, error) {
return getProvidersProxies(f.providers), nil
return getProvidersProxies(f.providers, touch), nil
})

return elm.([]C.Proxy)
}

func (f *Fallback) findAliveProxy() C.Proxy {
proxies := f.proxies()
func (f *Fallback) findAliveProxy(touch bool) C.Proxy {
proxies := f.proxies(touch)
for _, proxy := range proxies {
if proxy.Alive() {
return proxy
}
}

return f.proxies()[0]
return proxies[0]
}

func NewFallback(options *GroupCommonOption, providers []provider.ProxyProvider) *Fallback {
Expand Down
8 changes: 4 additions & 4 deletions adapters/outboundgroup/loadbalance.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,21 @@ func strategyConsistentHashing() strategyFn {
}

func (lb *LoadBalance) Unwrap(metadata *C.Metadata) C.Proxy {
proxies := lb.proxies()
proxies := lb.proxies(true)
return lb.strategyFn(proxies, metadata)
}

func (lb *LoadBalance) proxies() []C.Proxy {
func (lb *LoadBalance) proxies(touch bool) []C.Proxy {
elm, _, _ := lb.single.Do(func() (interface{}, error) {
return getProvidersProxies(lb.providers), nil
return getProvidersProxies(lb.providers, touch), nil
})

return elm.([]C.Proxy)
}

func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range lb.proxies() {
for _, proxy := range lb.proxies(false) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]interface{}{
Expand Down
11 changes: 7 additions & 4 deletions adapters/outboundgroup/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ type GroupCommonOption struct {
Use []string `group:"use,omitempty"`
URL string `group:"url,omitempty"`
Interval int `group:"interval,omitempty"`
Lazy bool `group:"lazy,omitempty"`
DisableUDP bool `group:"disable-udp,omitempty"`
}

func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy, providersMap map[string]provider.ProxyProvider) (C.ProxyAdapter, error) {
decoder := structure.NewDecoder(structure.Option{TagName: "group", WeaklyTypedInput: true})

groupOption := &GroupCommonOption{}
groupOption := &GroupCommonOption{
Lazy: true,
}
if err := decoder.Decode(config, groupOption); err != nil {
return nil, errFormat
}
Expand All @@ -55,7 +58,7 @@ func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy,

// if Use not empty, drop health check options
if len(groupOption.Use) != 0 {
hc := provider.NewHealthCheck(ps, "", 0)
hc := provider.NewHealthCheck(ps, "", 0, true)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
Expand All @@ -69,7 +72,7 @@ func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy,

// select don't need health check
if groupOption.Type == "select" || groupOption.Type == "relay" {
hc := provider.NewHealthCheck(ps, "", 0)
hc := provider.NewHealthCheck(ps, "", 0, true)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
Expand All @@ -82,7 +85,7 @@ func ParseProxyGroup(config map[string]interface{}, proxyMap map[string]C.Proxy,
return nil, errMissHealthCheck
}

hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval))
hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), groupOption.Lazy)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
return nil, err
Expand Down
12 changes: 6 additions & 6 deletions adapters/outboundgroup/relay.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Relay struct {
}

func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
proxies := r.proxies(metadata)
proxies := r.proxies(metadata, true)
if len(proxies) == 0 {
return nil, errors.New("proxy does not exist")
}
Expand Down Expand Up @@ -58,7 +58,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn,

func (r *Relay) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range r.rawProxies() {
for _, proxy := range r.rawProxies(false) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]interface{}{
Expand All @@ -67,16 +67,16 @@ func (r *Relay) MarshalJSON() ([]byte, error) {
})
}

func (r *Relay) rawProxies() []C.Proxy {
func (r *Relay) rawProxies(touch bool) []C.Proxy {
elm, _, _ := r.single.Do(func() (interface{}, error) {
return getProvidersProxies(r.providers), nil
return getProvidersProxies(r.providers, touch), nil
})

return elm.([]C.Proxy)
}

func (r *Relay) proxies(metadata *C.Metadata) []C.Proxy {
proxies := r.rawProxies()
func (r *Relay) proxies(metadata *C.Metadata, touch bool) []C.Proxy {
proxies := r.rawProxies(touch)

for n, proxy := range proxies {
subproxy := proxy.Unwrap(metadata)
Expand Down
18 changes: 9 additions & 9 deletions adapters/outboundgroup/selector.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ type Selector struct {
}

func (s *Selector) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
c, err := s.selectedProxy().DialContext(ctx, metadata)
c, err := s.selectedProxy(true).DialContext(ctx, metadata)
if err == nil {
c.AppendToChains(s)
}
return c, err
}

func (s *Selector) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
pc, err := s.selectedProxy().DialUDP(metadata)
pc, err := s.selectedProxy(true).DialUDP(metadata)
if err == nil {
pc.AppendToChains(s)
}
Expand All @@ -40,12 +40,12 @@ func (s *Selector) SupportUDP() bool {
return false
}

return s.selectedProxy().SupportUDP()
return s.selectedProxy(false).SupportUDP()
}

func (s *Selector) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range getProvidersProxies(s.providers) {
for _, proxy := range getProvidersProxies(s.providers, false) {
all = append(all, proxy.Name())
}

Expand All @@ -57,11 +57,11 @@ func (s *Selector) MarshalJSON() ([]byte, error) {
}

func (s *Selector) Now() string {
return s.selectedProxy().Name()
return s.selectedProxy(false).Name()
}

func (s *Selector) Set(name string) error {
for _, proxy := range getProvidersProxies(s.providers) {
for _, proxy := range getProvidersProxies(s.providers, false) {
if proxy.Name() == name {
s.selected = name
s.single.Reset()
Expand All @@ -73,12 +73,12 @@ func (s *Selector) Set(name string) error {
}

func (s *Selector) Unwrap(metadata *C.Metadata) C.Proxy {
return s.selectedProxy()
return s.selectedProxy(true)
}

func (s *Selector) selectedProxy() C.Proxy {
func (s *Selector) selectedProxy(touch bool) C.Proxy {
elm, _, _ := s.single.Do(func() (interface{}, error) {
proxies := getProvidersProxies(s.providers)
proxies := getProvidersProxies(s.providers, touch)
for _, proxy := range proxies {
if proxy.Name() == s.selected {
return proxy, nil
Expand Down
20 changes: 10 additions & 10 deletions adapters/outboundgroup/urltest.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,40 +30,40 @@ type URLTest struct {
}

func (u *URLTest) Now() string {
return u.fast().Name()
return u.fast(false).Name()
}

func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata) (c C.Conn, err error) {
c, err = u.fast().DialContext(ctx, metadata)
c, err = u.fast(true).DialContext(ctx, metadata)
if err == nil {
c.AppendToChains(u)
}
return c, err
}

func (u *URLTest) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
pc, err := u.fast().DialUDP(metadata)
pc, err := u.fast(true).DialUDP(metadata)
if err == nil {
pc.AppendToChains(u)
}
return pc, err
}

func (u *URLTest) Unwrap(metadata *C.Metadata) C.Proxy {
return u.fast()
return u.fast(true)
}

func (u *URLTest) proxies() []C.Proxy {
func (u *URLTest) proxies(touch bool) []C.Proxy {
elm, _, _ := u.single.Do(func() (interface{}, error) {
return getProvidersProxies(u.providers), nil
return getProvidersProxies(u.providers, touch), nil
})

return elm.([]C.Proxy)
}

func (u *URLTest) fast() C.Proxy {
func (u *URLTest) fast(touch bool) C.Proxy {
elm, _, _ := u.fastSingle.Do(func() (interface{}, error) {
proxies := u.proxies()
proxies := u.proxies(touch)
fast := proxies[0]
min := fast.LastDelay()
for _, proxy := range proxies[1:] {
Expand Down Expand Up @@ -94,12 +94,12 @@ func (u *URLTest) SupportUDP() bool {
return false
}

return u.fast().SupportUDP()
return u.fast(false).SupportUDP()
}

func (u *URLTest) MarshalJSON() ([]byte, error) {
var all []string
for _, proxy := range u.proxies() {
for _, proxy := range u.proxies(false) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]interface{}{
Expand Down
33 changes: 23 additions & 10 deletions adapters/provider/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"time"

C "github.com/Dreamacro/clash/constant"

"go.uber.org/atomic"
)

const (
Expand All @@ -17,10 +19,12 @@ type HealthCheckOption struct {
}

type HealthCheck struct {
url string
proxies []C.Proxy
interval uint
done chan struct{}
url string
proxies []C.Proxy
interval uint
lazy bool
lastTouch *atomic.Int64
done chan struct{}
}

func (hc *HealthCheck) process() {
Expand All @@ -30,7 +34,10 @@ func (hc *HealthCheck) process() {
for {
select {
case <-ticker.C:
hc.check()
now := time.Now().Unix()
if !hc.lazy || now-hc.lastTouch.Load() < int64(hc.interval) {
hc.check()
}
case <-hc.done:
ticker.Stop()
return
Expand All @@ -46,6 +53,10 @@ func (hc *HealthCheck) auto() bool {
return hc.interval != 0
}

func (hc *HealthCheck) touch() {
hc.lastTouch.Store(time.Now().Unix())
}

func (hc *HealthCheck) check() {
ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
for _, proxy := range hc.proxies {
Expand All @@ -60,11 +71,13 @@ func (hc *HealthCheck) close() {
hc.done <- struct{}{}
}

func NewHealthCheck(proxies []C.Proxy, url string, interval uint) *HealthCheck {
func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool) *HealthCheck {
return &HealthCheck{
proxies: proxies,
url: url,
interval: interval,
done: make(chan struct{}, 1),
proxies: proxies,
url: url,
interval: interval,
lazy: lazy,
lastTouch: atomic.NewInt64(0),
done: make(chan struct{}, 1),
}
}
Loading

0 comments on commit 0402878

Please sign in to comment.