strategy: added new option checkfailedonly #138

This commit is contained in:
nadoo 2020-04-21 00:50:12 +08:00
parent 149824cc20
commit 0b0611a0dc
4 changed files with 84 additions and 52 deletions

View File

@ -43,6 +43,7 @@ func confInit() {
flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80") flag.StringVar(&conf.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "proxy check interval(seconds)") flag.IntVar(&conf.StrategyConfig.CheckInterval, "checkinterval", 30, "proxy check interval(seconds)")
flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "proxy check timeout(seconds)") flag.IntVar(&conf.StrategyConfig.CheckTimeout, "checktimeout", 10, "proxy check timeout(seconds)")
flag.BoolVar(&conf.StrategyConfig.CheckFailedOnly, "checkfailedonly", false, "check failed fowarder only")
flag.IntVar(&conf.StrategyConfig.MaxFailures, "maxfailures", 3, "max failures to change forwarder status to disabled") flag.IntVar(&conf.StrategyConfig.MaxFailures, "maxfailures", 3, "max failures to change forwarder status to disabled")
flag.StringVar(&conf.StrategyConfig.IntFace, "interface", "", "source ip or source interface") flag.StringVar(&conf.StrategyConfig.IntFace, "interface", "", "source ip or source interface")

4
go.mod
View File

@ -10,9 +10,9 @@ require (
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/tjfoc/gmsm v1.3.0 // indirect github.com/tjfoc/gmsm v1.3.0 // indirect
github.com/xtaci/kcp-go/v5 v5.5.12 github.com/xtaci/kcp-go/v5 v5.5.12
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 golang.org/x/crypto v0.0.0-20200420104511-884d27f42877
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e // indirect
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
) )
// Replace dependency modules with local developing copy // Replace dependency modules with local developing copy

8
go.sum
View File

@ -58,8 +58,8 @@ golang.org/x/crypto v0.0.0-20191010185427-af544f31c8ac/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904 h1:bXoxMPcSLOq08zI3/c5dEBT6lE4eh+jOh886GHrn6V8= golang.org/x/crypto v0.0.0-20200420104511-884d27f42877 h1:IhZPbxNd1UjBCaD5AfpSSbJTRlp+ZSuyuH5uoksNS04=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200420104511-884d27f42877/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -73,8 +73,8 @@ golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191020212454-3e7259c5e7c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8=
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -17,12 +17,13 @@ import (
// Config is strategy config struct. // Config is strategy config struct.
type Config struct { type Config struct {
Strategy string Strategy string
CheckWebSite string CheckWebSite string
CheckInterval int CheckInterval int
CheckTimeout int CheckTimeout int
MaxFailures int CheckFailedOnly bool
IntFace string MaxFailures int
IntFace string
} }
// forwarder slice orderd by priority // forwarder slice orderd by priority
@ -185,66 +186,96 @@ func (p *Proxy) Check() {
// no need to check when there's only 1 forwarder // no need to check when there's only 1 forwarder
if len(p.fwdrs) > 1 { if len(p.fwdrs) > 1 {
for i := 0; i < len(p.fwdrs); i++ { for i := 0; i < len(p.fwdrs); i++ {
go p.check(i) go p.check(p.fwdrs[i])
} }
} }
} }
func (p *Proxy) check(i int) { func (p *Proxy) check(f *Forwarder) {
f := p.fwdrs[i] wait := uint8(0)
retry := 1
buf := make([]byte, 4) buf := make([]byte, 4)
intval := time.Duration(p.config.CheckInterval) * time.Second
for { for {
time.Sleep(time.Duration(p.config.CheckInterval) * time.Second * time.Duration(retry>>1)) time.Sleep(intval * time.Duration(wait))
// check all forwarders at least one time // check all forwarders at least one time
if retry > 1 && f.Priority() < p.Priority() { if wait > 0 && (f.Priority() < p.Priority()) {
continue continue
} }
retry <<= 1 if f.Enabled() && p.config.CheckFailedOnly {
if retry > 16 {
retry = 16
}
startTime := time.Now()
rc, err := f.Dial("tcp", p.config.CheckWebSite)
if err != nil {
f.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, err)
continue continue
} }
rc.Write([]byte("GET / HTTP/1.0\r\n\r\n")) if checkWebSite(f, p.config.CheckWebSite, time.Duration(p.config.CheckTimeout)*time.Second, buf) {
wait = 1
_, err = io.ReadFull(rc, buf) continue
if err != nil {
f.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, err)
} else if bytes.Equal([]byte("HTTP"), buf) {
readTime := time.Since(startTime)
f.SetLatency(int64(readTime))
if readTime > time.Duration(p.config.CheckTimeout)*time.Second {
f.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. check timeout: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, readTime)
} else {
retry = 2
f.Enable()
log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, readTime)
}
} else {
f.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", f.Addr(), f.Priority(), p.config.CheckWebSite, buf)
} }
rc.Close() if wait == 0 {
wait = 1
}
wait *= 2
if wait > 16 {
wait = 16
}
} }
} }
func checkWebSite(fwdr *Forwarder, website string, timeout time.Duration, buf []byte) bool {
startTime := time.Now()
rc, err := fwdr.Dial("tcp", website)
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in dial: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
defer rc.Close()
_, err = rc.Write([]byte("GET / HTTP/1.0\r\n\r\n"))
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in write: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
_, err = io.ReadFull(rc, buf)
if err != nil {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. error in read: %s", fwdr.Addr(), fwdr.Priority(),
website, err)
return false
}
if !bytes.Equal([]byte("HTTP"), buf) {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. server response: %s", fwdr.Addr(), fwdr.Priority(),
website, buf)
return false
}
readTime := time.Since(startTime)
fwdr.SetLatency(int64(readTime))
if readTime > timeout {
fwdr.Disable()
log.F("[check] %s(%d) -> %s, DISABLED. check timeout: %s", fwdr.Addr(), fwdr.Priority(),
website, readTime)
return false
}
fwdr.Enable()
log.F("[check] %s(%d) -> %s, ENABLED. connect time: %s", fwdr.Addr(), fwdr.Priority(),
website, readTime)
return true
}
// Round Robin // Round Robin
func (p *Proxy) scheduleRR(dstAddr string) *Forwarder { func (p *Proxy) scheduleRR(dstAddr string) *Forwarder {
return p.avail[atomic.AddUint32(&p.index, 1)%uint32(len(p.avail))] return p.avail[atomic.AddUint32(&p.index, 1)%uint32(len(p.avail))]