mirror of
https://github.com/umputun/reproxy.git
synced 2025-06-30 22:13:42 +02:00
add lb selector
This commit is contained in:
15
app/main.go
15
app/main.go
@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@ -31,6 +32,7 @@ var opts struct {
|
|||||||
MaxSize string `short:"m" long:"max" env:"MAX_SIZE" default:"64K" description:"max request size"`
|
MaxSize string `short:"m" long:"max" env:"MAX_SIZE" default:"64K" description:"max request size"`
|
||||||
GzipEnabled bool `short:"g" long:"gzip" env:"GZIP" description:"enable gz compression"`
|
GzipEnabled bool `short:"g" long:"gzip" env:"GZIP" description:"enable gz compression"`
|
||||||
ProxyHeaders []string `short:"x" long:"header" env:"HEADER" description:"proxy headers" env-delim:","`
|
ProxyHeaders []string `short:"x" long:"header" env:"HEADER" description:"proxy headers" env-delim:","`
|
||||||
|
LBType string `long:"lb-type" env:"LB_TYPE" description:"load balancer type" choice:"random" choice:"failover" default:"random"` //nolint
|
||||||
|
|
||||||
SSL struct {
|
SSL struct {
|
||||||
Type string `long:"type" env:"TYPE" description:"ssl (auto) support" choice:"none" choice:"static" choice:"auto" default:"none"` //nolint
|
Type string `long:"type" env:"TYPE" description:"ssl (auto) support" choice:"none" choice:"static" choice:"auto" default:"none"` //nolint
|
||||||
@ -239,6 +241,7 @@ func run() error {
|
|||||||
AccessLog: accessLog,
|
AccessLog: accessLog,
|
||||||
StdOutEnabled: opts.Logger.StdOut,
|
StdOutEnabled: opts.Logger.StdOut,
|
||||||
Signature: opts.Signature,
|
Signature: opts.Signature,
|
||||||
|
LBSelector: makeLBSelector(),
|
||||||
Timeouts: proxy.Timeouts{
|
Timeouts: proxy.Timeouts{
|
||||||
ReadHeader: opts.Timeouts.ReadHeader,
|
ReadHeader: opts.Timeouts.ReadHeader,
|
||||||
Write: opts.Timeouts.Write,
|
Write: opts.Timeouts.Write,
|
||||||
@ -308,6 +311,18 @@ func makeProviders() ([]discovery.Provider, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeLBSelector() func(len int) int {
|
||||||
|
switch opts.LBType {
|
||||||
|
case "random":
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
return rand.Intn
|
||||||
|
case "failover":
|
||||||
|
return func(int) int { return 0 } // dead server won't be in the list, we can safely pick the first one
|
||||||
|
default:
|
||||||
|
return func(int) int { return 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func makeSSLConfig() (config proxy.SSLConfig, err error) {
|
func makeSSLConfig() (config proxy.SSLConfig, err error) {
|
||||||
switch opts.SSL.Type {
|
switch opts.SSL.Type {
|
||||||
case "none":
|
case "none":
|
||||||
|
@ -41,6 +41,7 @@ type Http struct { // nolint golint
|
|||||||
CacheControl MiddlewareProvider
|
CacheControl MiddlewareProvider
|
||||||
Metrics MiddlewareProvider
|
Metrics MiddlewareProvider
|
||||||
Reporter Reporter
|
Reporter Reporter
|
||||||
|
LBSelector func(len int) int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Matcher source info (server and route) to the destination url
|
// Matcher source info (server and route) to the destination url
|
||||||
@ -84,6 +85,10 @@ func (h *Http) Run(ctx context.Context) error {
|
|||||||
log.Printf("[DEBUG] assets file server enabled for %s, webroot %s", h.AssetsLocation, h.AssetsWebRoot)
|
log.Printf("[DEBUG] assets file server enabled for %s, webroot %s", h.AssetsLocation, h.AssetsWebRoot)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if h.LBSelector == nil {
|
||||||
|
h.LBSelector = rand.Intn
|
||||||
|
}
|
||||||
|
|
||||||
var httpServer, httpsServer *http.Server
|
var httpServer, httpsServer *http.Server
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@ -113,8 +118,6 @@ func (h *Http) Run(ctx context.Context) error {
|
|||||||
h.gzipHandler(),
|
h.gzipHandler(),
|
||||||
)
|
)
|
||||||
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
|
|
||||||
if len(h.SSLConfig.FQDNs) == 0 && h.SSLConfig.SSLMode == SSLAuto {
|
if len(h.SSLConfig.FQDNs) == 0 && h.SSLConfig.SSLMode == SSLAuto {
|
||||||
// discovery async and may happen not right away. Try to get servers for some time
|
// discovery async and may happen not right away. Try to get servers for some time
|
||||||
for i := 0; i < 100; i++ {
|
for i := 0; i < 100; i++ {
|
||||||
@ -206,7 +209,7 @@ func (h *Http) proxyHandler() http.HandlerFunc {
|
|||||||
server = strings.Split(r.Host, ":")[0]
|
server = strings.Split(r.Host, ":")[0]
|
||||||
}
|
}
|
||||||
matches := h.Match(server, r.URL.Path) // get all matches for the server:path pair
|
matches := h.Match(server, r.URL.Path) // get all matches for the server:path pair
|
||||||
u, ok := h.getMatch(matches, rand.Intn)
|
u, ok := h.getMatch(matches) // pick a single match from alive only, uses LBSelector as the strategy
|
||||||
if !ok { // no route match
|
if !ok { // no route match
|
||||||
if h.isAssetRequest(r) {
|
if h.isAssetRequest(r) {
|
||||||
assetsHandler.ServeHTTP(w, r)
|
assetsHandler.ServeHTTP(w, r)
|
||||||
@ -244,24 +247,25 @@ func (h *Http) proxyHandler() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Http) getMatch(mm discovery.Matches, picker func(len int) int) (u string, ok bool) {
|
func (h *Http) getMatch(mm discovery.Matches) (u string, ok bool) {
|
||||||
if len(mm.Routes) == 0 {
|
if len(mm.Routes) == 0 {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
var urls []string
|
var urls []string // alive destinations only
|
||||||
for _, m := range mm.Routes {
|
for _, m := range mm.Routes {
|
||||||
if m.Alive {
|
if m.Alive {
|
||||||
urls = append(urls, m.Destination)
|
urls = append(urls, m.Destination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(urls) {
|
switch len(urls) {
|
||||||
case 0:
|
case 0:
|
||||||
return "", false
|
return "", false
|
||||||
case 1:
|
case 1:
|
||||||
return urls[0], true
|
return urls[0], true
|
||||||
default:
|
default:
|
||||||
return urls[picker(len(urls))], true
|
return urls[h.LBSelector(len(urls))], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,10 +408,10 @@ func TestHttp_getMatch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
h := Http{}
|
h := Http{LBSelector: func(len int) int { return 0 }}
|
||||||
for i, tt := range tbl {
|
for i, tt := range tbl {
|
||||||
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
res, ok := h.getMatch(tt.matches, func(len int) int { return 0 })
|
res, ok := h.getMatch(tt.matches)
|
||||||
require.Equal(t, tt.ok, ok)
|
require.Equal(t, tt.ok, ok)
|
||||||
assert.Equal(t, tt.res, res)
|
assert.Equal(t, tt.res, res)
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user