glider/strategy/strategy.go

219 lines
4.8 KiB
Go
Raw Normal View History

2018-08-11 11:46:10 +08:00
package strategy
2017-07-13 21:55:41 +08:00
import (
"bytes"
"io"
"net"
"sort"
"strings"
2018-01-12 20:11:21 +08:00
"time"
"github.com/nadoo/glider/common/log"
"github.com/nadoo/glider/proxy"
)
2018-08-11 11:46:10 +08:00
// Config of strategy
type Config struct {
Strategy string
CheckWebSite string
CheckInterval int
MaxFailures int
}
2018-08-11 11:46:10 +08:00
// NewDialer returns a new strategy dialer
func NewDialer(s []string, c *Config) proxy.Dialer {
// global forwarders in xx.conf
var fwdrs []*proxy.Forwarder
for _, chain := range s {
fwdr, err := proxy.ForwarderFromURL(chain)
if err != nil {
log.Fatal(err)
}
fwdr.MaxFailures = uint32(c.MaxFailures)
fwdrs = append(fwdrs, fwdr)
}
if len(fwdrs) == 0 {
return proxy.Direct
2018-01-13 20:08:49 +08:00
}
if len(fwdrs) == 1 {
return fwdrs[0]
2018-01-13 20:08:49 +08:00
}
var dialer proxy.Dialer
switch c.Strategy {
2018-01-13 20:08:49 +08:00
case "rr":
dialer = newRRDialer(fwdrs, c.CheckWebSite, c.CheckInterval)
log.F("forward to remote servers in round robin mode.")
2018-01-13 20:08:49 +08:00
case "ha":
dialer = newHADialer(fwdrs, c.CheckWebSite, c.CheckInterval)
log.F("forward to remote servers in high availability mode.")
2018-01-13 20:08:49 +08:00
default:
log.F("not supported forward mode '%s', just use the first forward server.", c.Strategy)
dialer = fwdrs[0]
}
return dialer
}
type forwarderSlice []*proxy.Forwarder
func (p forwarderSlice) Len() int { return len(p) }
func (p forwarderSlice) Less(i, j int) bool { return p[i].Priority > p[j].Priority }
func (p forwarderSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
// rrDialer is a rr dialer
type rrDialer struct {
fwdrs forwarderSlice
idx int
priority int
// 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
func newRRDialer(fs []*proxy.Forwarder, website string, interval int) *rrDialer {
rr := &rrDialer{fwdrs: fs}
rr.website = website
if strings.IndexByte(rr.website, ':') == -1 {
rr.website += ":80"
}
2018-01-13 20:08:49 +08:00
rr.interval = interval
sort.Sort(rr.fwdrs)
rr.priority = rr.fwdrs[0].Priority
for k := range rr.fwdrs {
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-17 00:26:38 +08:00
func (rr *rrDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
return rr.NextDialer(addr).DialUDP(network, addr)
}
func (rr *rrDialer) nextDialer(dstAddr string) *proxy.Forwarder {
n := len(rr.fwdrs)
2017-07-13 21:55:41 +08:00
if n == 1 {
rr.idx = 0
2017-07-13 21:55:41 +08:00
}
for _, fwder := range rr.fwdrs {
if fwder.Enabled() {
rr.priority = fwder.Priority
break
}
}
if rr.fwdrs[rr.idx].Priority < rr.priority {
rr.idx = 0
}
found := false
for i := 0; i < n; i++ {
rr.idx = (rr.idx + 1) % n
if rr.fwdrs[rr.idx].Enabled() &&
rr.fwdrs[rr.idx].Priority >= rr.priority {
found = true
rr.priority = rr.fwdrs[rr.idx].Priority
break
}
}
if !found {
log.F("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.")
2017-07-13 21:55:41 +08:00
}
return rr.fwdrs[rr.idx]
}
func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer {
return rr.nextDialer(dstAddr)
2017-07-13 21:55:41 +08:00
}
// Check dialer
func (rr *rrDialer) checkDialer(idx int) {
retry := 1
buf := make([]byte, 4)
d := rr.fwdrs[idx]
for {
2018-01-13 20:08:49 +08:00
time.Sleep(time.Duration(rr.interval) * time.Second * time.Duration(retry>>1))
2017-07-30 01:54:19 +08:00
// check forwarders whose priority not less than current priority only
if d.Priority < rr.priority {
continue
}
retry <<= 1
if retry > 16 {
retry = 16
}
startTime := time.Now()
c, err := d.Dial("tcp", rr.website)
if err != nil {
rr.fwdrs[idx].Disable()
2018-06-28 23:20:04 +08:00
log.F("[check] %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err)
continue
}
2018-01-29 23:33:53 +08:00
c.Write([]byte("GET / HTTP/1.0\r\n\r\n"))
_, err = io.ReadFull(c, buf)
if err != nil {
rr.fwdrs[idx].Disable()
2018-06-28 23:20:04 +08:00
log.F("[check] %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err)
} else if bytes.Equal([]byte("HTTP"), buf) {
rr.fwdrs[idx].Enable()
retry = 2
dialTime := time.Since(startTime)
2018-06-28 23:20:04 +08:00
log.F("[check] %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String())
} else {
rr.fwdrs[idx].Disable()
2018-06-28 23:20:04 +08:00
log.F("[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 []*proxy.Forwarder, webhost string, duration int) proxy.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.fwdrs[ha.idx]
if !d.Enabled() {
d = ha.nextDialer(addr)
2017-07-13 21:55:41 +08:00
}
return d.Dial(network, addr)
}
2018-01-17 00:26:38 +08:00
func (ha *haDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
d := ha.fwdrs[ha.idx]
if !d.Enabled() {
d = ha.nextDialer(addr)
2018-01-17 00:26:38 +08:00
}
return d.DialUDP(network, addr)
}