glider/strategy.go

165 lines
3.5 KiB
Go
Raw Normal View History

2017-07-13 21:55:41 +08:00
package main
import (
"bytes"
"io"
"net"
"strings"
2017-11-03 18:55:15 +08:00
"sync"
2018-01-12 20:11:21 +08:00
"time"
)
// NewStrategyDialer returns a new Strategy Dialer
2018-01-13 20:08:49 +08:00
func NewStrategyDialer(strategy string, dialers []Dialer, website string, interval int) Dialer {
if len(dialers) == 0 {
2018-01-13 20:08:49 +08:00
return Direct
}
if len(dialers) == 1 {
return dialers[0]
}
var dialer Dialer
switch strategy {
case "rr":
dialer = newRRDialer(dialers, website, interval)
logf("forward to remote servers in round robin mode.")
case "ha":
dialer = newHADialer(dialers, website, interval)
logf("forward to remote servers in high availability mode.")
default:
logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy)
dialer = dialers[0]
}
return dialer
}
// rrDialer is the base struct of strategy dialer
type rrDialer struct {
dialers []Dialer
idx int
2017-11-03 18:55:15 +08:00
status sync.Map
// for checking
website string
2018-01-13 20:08:49 +08:00
interval int
2017-07-13 21:55:41 +08:00
}
// newRRDialer returns a new rrDialer
2018-01-13 20:08:49 +08:00
func newRRDialer(dialers []Dialer, website string, interval int) *rrDialer {
rr := &rrDialer{dialers: dialers}
rr.website = website
2018-01-13 20:08:49 +08:00
rr.interval = interval
for k := range dialers {
2018-01-12 20:11:21 +08:00
rr.status.Store(k, true)
go rr.checkDialer(k)
2017-07-13 21:55:41 +08:00
}
return rr
2017-07-13 21:55:41 +08:00
}
func (rr *rrDialer) Addr() string { return "STRATEGY" }
func (rr *rrDialer) Dial(network, addr string) (net.Conn, error) {
return rr.NextDialer(addr).Dial(network, addr)
}
2017-07-13 21:55:41 +08:00
2018-01-12 20:11:21 +08:00
func (rr *rrDialer) DialUDP(network, addr string) (net.PacketConn, error) {
return rr.NextDialer(addr).DialUDP(network, addr)
}
func (rr *rrDialer) NextDialer(dstAddr string) Dialer {
n := len(rr.dialers)
2017-07-13 21:55:41 +08:00
if n == 1 {
rr.idx = 0
2017-07-13 21:55:41 +08:00
}
found := false
for i := 0; i < n; i++ {
rr.idx = (rr.idx + 1) % n
2017-11-03 18:55:15 +08:00
result, ok := rr.status.Load(rr.idx)
2018-01-12 20:11:21 +08:00
if ok && result.(bool) {
found = true
break
}
}
if !found {
2017-07-16 20:13:01 +08:00
logf("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
2017-07-13 21:55:41 +08:00
}
return rr.dialers[rr.idx]
2017-07-13 21:55:41 +08:00
}
// Check dialer
func (rr *rrDialer) checkDialer(idx int) {
retry := 1
buf := make([]byte, 4)
if strings.IndexByte(rr.website, ':') == -1 {
rr.website = rr.website + ":80"
}
d := rr.dialers[idx]
for {
2018-01-13 20:08:49 +08:00
time.Sleep(time.Duration(rr.interval) * time.Second * time.Duration(retry>>1))
retry <<= 1
2017-07-30 01:54:19 +08:00
if retry > 16 {
retry = 16
}
startTime := time.Now()
c, err := d.Dial("tcp", rr.website)
if err != nil {
2017-11-03 18:55:15 +08:00
rr.status.Store(idx, false)
logf("proxy-check %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err)
continue
}
c.Write([]byte("GET / HTTP/1.0"))
c.Write([]byte("\r\n\r\n"))
_, err = io.ReadFull(c, buf)
if err != nil {
2017-11-03 18:55:15 +08:00
rr.status.Store(idx, false)
logf("proxy-check %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err)
} else if bytes.Equal([]byte("HTTP"), buf) {
2017-11-03 18:55:15 +08:00
rr.status.Store(idx, true)
retry = 2
dialTime := time.Since(startTime)
logf("proxy-check %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String())
} else {
2017-11-03 18:55:15 +08:00
rr.status.Store(idx, false)
logf("proxy-check %s -> %s, set to DISABLED. server response: %s", d.Addr(), rr.website, buf)
}
c.Close()
}
2017-07-13 21:55:41 +08:00
}
// high availability proxy
type haDialer struct {
*rrDialer
2017-07-13 21:55:41 +08:00
}
// newHADialer .
func newHADialer(dialers []Dialer, webhost string, duration int) Dialer {
return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)}
2017-07-13 21:55:41 +08:00
}
func (ha *haDialer) Dial(network, addr string) (net.Conn, error) {
d := ha.dialers[ha.idx]
2017-11-03 18:55:15 +08:00
result, ok := ha.status.Load(ha.idx)
2018-01-12 20:11:21 +08:00
if ok && !result.(bool) {
d = ha.NextDialer(addr)
2017-07-13 21:55:41 +08:00
}
return d.Dial(network, addr)
}