1
0
Fork 0

Resync oxy with original repository

This commit is contained in:
SALLEYRON Julien 2017-11-22 18:20:03 +01:00 committed by Traefiker
parent da5e4a13bf
commit bee8ebb00b
31 changed files with 650 additions and 808 deletions

View file

@ -0,0 +1,5 @@
package roundrobin
import "net/http"
type RequestRewriteListener func(oldReq *http.Request, newReq *http.Request)

View file

@ -7,6 +7,7 @@ import (
"sync"
"time"
log "github.com/Sirupsen/logrus"
"github.com/mailgun/timetools"
"github.com/vulcand/oxy/memmetrics"
"github.com/vulcand/oxy/utils"
@ -42,22 +43,15 @@ type Rebalancer struct {
// errHandler is HTTP handler called in case of errors
errHandler utils.ErrorHandler
log utils.Logger
ratings []float64
// creates new meters
newMeter NewMeterFn
// sticky session object
ss *StickySession
}
stickySession *StickySession
func RebalancerLogger(log utils.Logger) RebalancerOption {
return func(r *Rebalancer) error {
r.log = log
return nil
}
requestRewriteListener RequestRewriteListener
}
func RebalancerClock(clock timetools.TimeProvider) RebalancerOption {
@ -89,18 +83,26 @@ func RebalancerErrorHandler(h utils.ErrorHandler) RebalancerOption {
}
}
func RebalancerStickySession(ss *StickySession) RebalancerOption {
func RebalancerStickySession(stickySession *StickySession) RebalancerOption {
return func(r *Rebalancer) error {
r.ss = ss
r.stickySession = stickySession
return nil
}
}
// RebalancerErrorHandler is a functional argument that sets error handler of the server
func RebalancerRequestRewriteListener(rrl RequestRewriteListener) RebalancerOption {
return func(r *Rebalancer) error {
r.requestRewriteListener = rrl
return nil
}
}
func NewRebalancer(handler balancerHandler, opts ...RebalancerOption) (*Rebalancer, error) {
rb := &Rebalancer{
mtx: &sync.Mutex{},
next: handler,
ss: nil,
mtx: &sync.Mutex{},
next: handler,
stickySession: nil,
}
for _, o := range opts {
if err := o(rb); err != nil {
@ -113,9 +115,6 @@ func NewRebalancer(handler balancerHandler, opts ...RebalancerOption) (*Rebalanc
if rb.backoffDuration == 0 {
rb.backoffDuration = 10 * time.Second
}
if rb.log == nil {
rb.log = &utils.NOPLogger{}
}
if rb.newMeter == nil {
rb.newMeter = func() (Meter, error) {
rc, err := memmetrics.NewRatioCounter(10, time.Second, memmetrics.RatioClock(rb.clock))
@ -143,6 +142,12 @@ func (rb *Rebalancer) Servers() []*url.URL {
}
func (rb *Rebalancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if log.GetLevel() >= log.DebugLevel {
logEntry := log.WithField("Request", utils.DumpHttpRequest(req))
logEntry.Debug("vulcand/oxy/roundrobin/rebalancer: begin ServeHttp on request")
defer logEntry.Debug("vulcand/oxy/roundrobin/rebalancer: competed ServeHttp on request")
}
pw := &utils.ProxyWriter{W: w}
start := rb.clock.UtcNow()
@ -150,16 +155,15 @@ func (rb *Rebalancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
newReq := *req
stuck := false
if rb.ss != nil {
cookie_url, present, err := rb.ss.GetBackend(&newReq, rb.Servers())
if rb.stickySession != nil {
cookieUrl, present, err := rb.stickySession.GetBackend(&newReq, rb.Servers())
if err != nil {
rb.errHandler.ServeHTTP(w, req, err)
return
log.Infof("vulcand/oxy/roundrobin/rebalancer: error using server from cookie: %v", err)
}
if present {
newReq.URL = cookie_url
newReq.URL = cookieUrl
stuck = true
}
}
@ -171,12 +175,23 @@ func (rb *Rebalancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
if rb.ss != nil {
rb.ss.StickBackend(url, &w)
if log.GetLevel() >= log.DebugLevel {
//log which backend URL we're sending this request to
log.WithFields(log.Fields{"Request": utils.DumpHttpRequest(req), "ForwardURL": url}).Debugf("vulcand/oxy/roundrobin/rebalancer: Forwarding this request to URL")
}
if rb.stickySession != nil {
rb.stickySession.StickBackend(url, &w)
}
newReq.URL = url
}
//Emit event to a listener if one exists
if rb.requestRewriteListener != nil {
rb.requestRewriteListener(req, &newReq)
}
rb.next.Next().ServeHTTP(pw, &newReq)
rb.recordMetrics(newReq.URL, pw.Code, rb.clock.UtcNow().Sub(start))
@ -262,11 +277,11 @@ func (rb *Rebalancer) upsertServer(u *url.URL, weight int) error {
return nil
}
func (r *Rebalancer) findServer(u *url.URL) (*rbServer, int) {
if len(r.servers) == 0 {
func (rb *Rebalancer) findServer(u *url.URL) (*rbServer, int) {
if len(rb.servers) == 0 {
return nil, -1
}
for i, s := range r.servers {
for i, s := range rb.servers {
if sameURL(u, s.url) {
return s, i
}
@ -304,7 +319,7 @@ func (rb *Rebalancer) adjustWeights() {
func (rb *Rebalancer) applyWeights() {
for _, srv := range rb.servers {
rb.log.Infof("upsert server %v, weight %v", srv.url, srv.curWeight)
log.Infof("upsert server %v, weight %v", srv.url, srv.curWeight)
rb.next.UpsertServer(srv.url, Weight(srv.curWeight))
}
}
@ -316,7 +331,7 @@ func (rb *Rebalancer) setMarkedWeights() bool {
if srv.good {
weight := increase(srv.curWeight)
if weight <= FSMMaxWeight {
rb.log.Infof("increasing weight of %v from %v to %v", srv.url, srv.curWeight, weight)
log.Infof("increasing weight of %v from %v to %v", srv.url, srv.curWeight, weight)
srv.curWeight = weight
changed = true
}
@ -363,13 +378,13 @@ func (rb *Rebalancer) markServers() bool {
}
}
if len(g) != 0 && len(b) != 0 {
rb.log.Infof("bad: %v good: %v, ratings: %v", b, g, rb.ratings)
log.Infof("bad: %v good: %v, ratings: %v", b, g, rb.ratings)
}
return len(g) != 0 && len(b) != 0
}
func (rb *Rebalancer) convergeWeights() bool {
// If we have previoulsy changed servers try to restore weights to the original state
// If we have previously changed servers try to restore weights to the original state
changed := false
for _, s := range rb.servers {
if s.origWeight == s.curWeight {
@ -377,7 +392,7 @@ func (rb *Rebalancer) convergeWeights() bool {
}
changed = true
newWeight := decrease(s.origWeight, s.curWeight)
rb.log.Infof("decreasing weight of %v from %v to %v", s.url, s.curWeight, newWeight)
log.Infof("decreasing weight of %v from %v to %v", s.url, s.curWeight, newWeight)
s.curWeight = newWeight
}
if !changed {

View file

@ -7,6 +7,7 @@ import (
"net/url"
"sync"
log "github.com/Sirupsen/logrus"
"github.com/vulcand/oxy/utils"
)
@ -29,9 +30,17 @@ func ErrorHandler(h utils.ErrorHandler) LBOption {
}
}
func EnableStickySession(ss *StickySession) LBOption {
func EnableStickySession(stickySession *StickySession) LBOption {
return func(s *RoundRobin) error {
s.ss = ss
s.stickySession = stickySession
return nil
}
}
// ErrorHandler is a functional argument that sets error handler of the server
func RoundRobinRequestRewriteListener(rrl RequestRewriteListener) LBOption {
return func(s *RoundRobin) error {
s.requestRewriteListener = rrl
return nil
}
}
@ -41,19 +50,20 @@ type RoundRobin struct {
next http.Handler
errHandler utils.ErrorHandler
// Current index (starts from -1)
index int
servers []*server
currentWeight int
ss *StickySession
index int
servers []*server
currentWeight int
stickySession *StickySession
requestRewriteListener RequestRewriteListener
}
func New(next http.Handler, opts ...LBOption) (*RoundRobin, error) {
rr := &RoundRobin{
next: next,
index: -1,
mutex: &sync.Mutex{},
servers: []*server{},
ss: nil,
next: next,
index: -1,
mutex: &sync.Mutex{},
servers: []*server{},
stickySession: nil,
}
for _, o := range opts {
if err := o(rr); err != nil {
@ -71,19 +81,24 @@ func (r *RoundRobin) Next() http.Handler {
}
func (r *RoundRobin) ServeHTTP(w http.ResponseWriter, req *http.Request) {
if log.GetLevel() >= log.DebugLevel {
logEntry := log.WithField("Request", utils.DumpHttpRequest(req))
logEntry.Debug("vulcand/oxy/roundrobin/rr: begin ServeHttp on request")
defer logEntry.Debug("vulcand/oxy/roundrobin/rr: competed ServeHttp on request")
}
// make shallow copy of request before chaning anything to avoid side effects
newReq := *req
stuck := false
if r.ss != nil {
cookie_url, present, err := r.ss.GetBackend(&newReq, r.Servers())
if r.stickySession != nil {
cookieURL, present, err := r.stickySession.GetBackend(&newReq, r.Servers())
if err != nil {
r.errHandler.ServeHTTP(w, req, err)
return
log.Infof("vulcand/oxy/roundrobin/rr: error using server from cookie: %v", err)
}
if present {
newReq.URL = cookie_url
newReq.URL = cookieURL
stuck = true
}
}
@ -95,11 +110,22 @@ func (r *RoundRobin) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
if r.ss != nil {
r.ss.StickBackend(url, &w)
if r.stickySession != nil {
r.stickySession.StickBackend(url, &w)
}
newReq.URL = url
}
if log.GetLevel() >= log.DebugLevel {
//log which backend URL we're sending this request to
log.WithFields(log.Fields{"Request": utils.DumpHttpRequest(req), "ForwardURL": newReq.URL}).Debugf("vulcand/oxy/roundrobin/rr: Forwarding this request to URL")
}
//Emit event to a listener if one exists
if r.requestRewriteListener != nil {
r.requestRewriteListener(req, &newReq)
}
r.next.ServeHTTP(w, &newReq)
}
@ -144,8 +170,6 @@ func (r *RoundRobin) nextServer() (*server, error) {
return srv, nil
}
}
// We did full circle and found no available servers
return nil, fmt.Errorf("no available servers")
}
func (r *RoundRobin) RemoveServer(u *url.URL) error {

View file

@ -7,16 +7,16 @@ import (
)
type StickySession struct {
cookiename string
cookieName string
}
func NewStickySession(c string) *StickySession {
return &StickySession{c}
func NewStickySession(cookieName string) *StickySession {
return &StickySession{cookieName}
}
// GetBackend returns the backend URL stored in the sticky cookie, iff the backend is still in the valid list of servers.
func (s *StickySession) GetBackend(req *http.Request, servers []*url.URL) (*url.URL, bool, error) {
cookie, err := req.Cookie(s.cookiename)
cookie, err := req.Cookie(s.cookieName)
switch err {
case nil:
case http.ErrNoCookie:
@ -25,22 +25,21 @@ func (s *StickySession) GetBackend(req *http.Request, servers []*url.URL) (*url.
return nil, false, err
}
s_url, err := url.Parse(cookie.Value)
serverURL, err := url.Parse(cookie.Value)
if err != nil {
return nil, false, err
}
if s.isBackendAlive(s_url, servers) {
return s_url, true, nil
if s.isBackendAlive(serverURL, servers) {
return serverURL, true, nil
} else {
return nil, false, nil
}
}
func (s *StickySession) StickBackend(backend *url.URL, w *http.ResponseWriter) {
c := &http.Cookie{Name: s.cookiename, Value: backend.String(), Path: "/"}
http.SetCookie(*w, c)
return
cookie := &http.Cookie{Name: s.cookieName, Value: backend.String(), Path: "/"}
http.SetCookie(*w, cookie)
}
func (s *StickySession) isBackendAlive(needle *url.URL, haystack []*url.URL) bool {
@ -48,8 +47,8 @@ func (s *StickySession) isBackendAlive(needle *url.URL, haystack []*url.URL) boo
return false
}
for _, s := range haystack {
if sameURL(needle, s) {
for _, serverURL := range haystack {
if sameURL(needle, serverURL) {
return true
}
}