Circuit breaker middleware

This commit is contained in:
emile 2015-09-19 13:02:59 +02:00
parent 9d46c5017c
commit 616c58ad4b
5 changed files with 97 additions and 40 deletions

View file

@ -49,3 +49,7 @@ image: build
dist:
mkdir dist
run-dev:
go build
./traefik

43
adapters.go Normal file
View file

@ -0,0 +1,43 @@
/*
Copyright
*/
package main
import (
"net/http"
"github.com/mailgun/oxy/utils"
"github.com/gorilla/mux"
)
type OxyLogger struct{
}
func (oxylogger *OxyLogger) Infof(format string, args ...interface{}) {
log.Debug(format, args...)
}
func (oxylogger *OxyLogger) Warningf(format string, args ...interface{}) {
log.Warning(format, args...)
}
func (oxylogger *OxyLogger) Errorf(format string, args ...interface{}) {
log.Error(format, args...)
}
type ErrorHandler struct {
}
func (e *ErrorHandler) ServeHTTP(w http.ResponseWriter, req *http.Request, err error) {
log.Error("server error ", err.Error())
utils.DefaultHandler.ServeHTTP(w, req, err)
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
//templatesRenderer.HTML(w, http.StatusNotFound, "notFound", nil)
}
func LoadDefaultConfig(gloablConfiguration *GlobalConfiguration) *mux.Router {
router := mux.NewRouter()
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
return router
}

View file

@ -172,7 +172,7 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration {
err = tmpl.Execute(&buffer, templateObjects)
if err != nil {
log.Error("Error with docker template:", err)
log.Error("Error with marathon template:", err)
return nil
}

21
middlewares/cbreaker.go Normal file
View file

@ -0,0 +1,21 @@
/*
Copyright
*/
package middlewares
import (
"net/http"
"github.com/mailgun/oxy/cbreaker"
)
type CircuitBreaker struct {
circuitBreaker *cbreaker.CircuitBreaker
}
func NewCircuitBreaker(next http.Handler, options ...cbreaker.CircuitBreakerOption) *CircuitBreaker {
circuitBreaker, _ := cbreaker.New(next, "NetworkErrorRatio() > 0.5", options...)
return &CircuitBreaker{circuitBreaker}
}
func (cb *CircuitBreaker) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
cb.circuitBreaker.ServeHTTP(rw, r)
}

View file

@ -19,6 +19,8 @@ import (
"reflect"
"syscall"
"time"
fmtlog "log"
"github.com/mailgun/oxy/cbreaker"
)
var (
@ -34,23 +36,9 @@ var (
})
)
type OxyLogger struct{
}
func (oxylogger *OxyLogger) Infof(format string, args ...interface{}) {
log.Info(format, args...)
}
func (oxylogger *OxyLogger) Warningf(format string, args ...interface{}) {
log.Warning(format, args...)
}
func (oxylogger *OxyLogger) Errorf(format string, args ...interface{}) {
log.Error(format, args...)
}
func main() {
kingpin.Parse()
fmtlog.SetFlags(fmtlog.Lshortfile | fmtlog.LstdFlags)
var srv *graceful.Server
var configurationRouter *mux.Router
var configurationChan = make(chan *Configuration)
@ -107,10 +95,15 @@ func main() {
} else if reflect.DeepEqual(currentConfiguration, configuration) {
log.Info("Skipping same configuration")
} else {
currentConfiguration = configuration
configurationRouter = LoadConfig(configuration, gloablConfiguration)
srv.Stop(time.Duration(gloablConfiguration.GraceTimeOut) * time.Second)
time.Sleep(3 * time.Second)
newConfigurationRouter, err := LoadConfig(configuration, gloablConfiguration)
if (err == nil ){
currentConfiguration = configuration
configurationRouter = newConfigurationRouter
srv.Stop(time.Duration(gloablConfiguration.GraceTimeOut) * time.Second)
time.Sleep(3 * time.Second)
}else{
log.Error("Error loading new configuration, aborted ", err)
}
}
}
}()
@ -132,6 +125,7 @@ func main() {
if gloablConfiguration.Web != nil {
providers = append(providers, gloablConfiguration.Web)
}
// providers = append(providers, NewConsulProvider())
// start providers
for _, provider := range providers {
@ -159,6 +153,7 @@ func main() {
var negroni = negroni.New()
negroni.Use(metrics)
negroni.Use(loggerMiddleware)
//negroni.Use(middlewares.NewCircuitBreaker(oxyLogger))
//negroni.Use(middlewares.NewRoutes(configurationRouter))
negroni.UseHandler(configurationRouter)
@ -185,27 +180,16 @@ func main() {
}
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
//templatesRenderer.HTML(w, http.StatusNotFound, "notFound", nil)
}
func LoadDefaultConfig(gloablConfiguration *GlobalConfiguration) *mux.Router {
router := mux.NewRouter()
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
return router
}
func LoadConfig(configuration *Configuration, gloablConfiguration *GlobalConfiguration) *mux.Router {
func LoadConfig(configuration *Configuration, gloablConfiguration *GlobalConfiguration) (*mux.Router, error) {
router := mux.NewRouter()
router.NotFoundHandler = http.HandlerFunc(notFoundHandler)
backends := map[string]http.Handler{}
for frontendName, frontend := range configuration.Frontends {
log.Debug("Creating frontend %s", frontendName)
fwd, _ := forward.New()
fwd, _ := forward.New(forward.Logger(oxyLogger))
newRoute := router.NewRoute().Name(frontendName)
for routeName, route := range frontend.Routes {
log.Debug("Creating route %s", routeName)
log.Debug("Creating route %s %s:%s", routeName, route.Rule, route.Value)
newRouteReflect := Invoke(newRoute, route.Rule, route.Value)
newRoute = newRouteReflect[0].Interface().(*mux.Route)
}
@ -214,22 +198,27 @@ func LoadConfig(configuration *Configuration, gloablConfiguration *GlobalConfigu
lb, _ := roundrobin.New(fwd)
rb, _ := roundrobin.NewRebalancer(lb, roundrobin.RebalancerLogger(oxyLogger))
for serverName, server := range configuration.Backends[frontend.Backend].Servers {
log.Debug("Creating server %s", serverName)
url, _ := url.Parse(server.Url)
rb.UpsertServer(url, roundrobin.Weight(server.Weight))
if url, err := url.Parse(server.Url); err != nil{
return nil, err
}else{
log.Debug("Creating server %s %s", serverName, url.String())
rb.UpsertServer(url, roundrobin.Weight(server.Weight))
}
}
backends[frontend.Backend] = lb
backends[frontend.Backend] = rb
} else {
log.Debug("Reusing backend %s", frontend.Backend)
}
// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(gloablConfiguration.Replay)), stream.Logger(oxyLogger))
newRoute.Handler(backends[frontend.Backend])
var negroni = negroni.New()
negroni.Use(middlewares.NewCircuitBreaker(backends[frontend.Backend], cbreaker.Logger(oxyLogger)))
newRoute.Handler(negroni)
err := newRoute.GetError()
if err != nil {
log.Error("Error building route ", err)
}
}
return router
return router, nil
}
func Invoke(any interface{}, name string, args ...interface{}) []reflect.Value {