add handler switcher instead of Manners
Signed-off-by: Emile Vauge <emile@vauge.com>
This commit is contained in:
parent
0e683cc535
commit
4042938556
4 changed files with 63 additions and 20 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,3 +8,4 @@ traefik.toml
|
||||||
*.test
|
*.test
|
||||||
vendor/
|
vendor/
|
||||||
static/
|
static/
|
||||||
|
glide.lock
|
|
@ -16,13 +16,14 @@ It supports several backends ([Docker :whale:](https://www.docker.com/), [Mesos/
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- It's fast
|
||||||
- No dependency hell, single binary made with go
|
- No dependency hell, single binary made with go
|
||||||
- Simple json Rest API
|
- Simple json Rest API
|
||||||
- Simple TOML file configuration
|
- Simple TOML file configuration
|
||||||
- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
|
- Multiple backends supported: Docker, Mesos/Marathon, Consul, Etcd, and more to come
|
||||||
- Watchers for backends, can listen change in backends to apply a new configuration automatically
|
- Watchers for backends, can listen change in backends to apply a new configuration automatically
|
||||||
- Hot-reloading of configuration. No need to restart the process
|
- Hot-reloading of configuration. No need to restart the process
|
||||||
- Graceful shutdown http connections during hot-reloads
|
- Graceful shutdown http connections
|
||||||
- Circuit breakers on backends
|
- Circuit breakers on backends
|
||||||
- Round Robin, rebalancer load-balancers
|
- Round Robin, rebalancer load-balancers
|
||||||
- Rest Metrics
|
- Rest Metrics
|
||||||
|
|
40
middlewares/handlerSwitcher.go
Normal file
40
middlewares/handlerSwitcher.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package middlewares
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HandlerSwitcher allows hot switching of http.ServeMux
|
||||||
|
type HandlerSwitcher struct {
|
||||||
|
handler *mux.Router
|
||||||
|
handlerLock *sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandlerSwitcher builds a new instance of HandlerSwitcher
|
||||||
|
func NewHandlerSwitcher(newHandler *mux.Router) (hs *HandlerSwitcher) {
|
||||||
|
return &HandlerSwitcher{
|
||||||
|
handler: newHandler,
|
||||||
|
handlerLock: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (hs *HandlerSwitcher) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
hs.handlerLock.Lock()
|
||||||
|
handlerBackup := hs.handler
|
||||||
|
hs.handlerLock.Unlock()
|
||||||
|
handlerBackup.ServeHTTP(rw, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHandler returns the current http.ServeMux
|
||||||
|
func (hs *HandlerSwitcher) GetHandler() (newHandler *mux.Router) {
|
||||||
|
return hs.handler
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateHandler safely updates the current http.ServeMux with a new one
|
||||||
|
func (hs *HandlerSwitcher) UpdateHandler(newHandler *mux.Router) {
|
||||||
|
hs.handlerLock.Lock()
|
||||||
|
hs.handler = newHandler
|
||||||
|
defer hs.handlerLock.Unlock()
|
||||||
|
}
|
27
server.go
27
server.go
|
@ -48,7 +48,7 @@ type Server struct {
|
||||||
|
|
||||||
type serverEntryPoint struct {
|
type serverEntryPoint struct {
|
||||||
httpServer *manners.GracefulServer
|
httpServer *manners.GracefulServer
|
||||||
httpRouter *mux.Router
|
httpRouter *middlewares.HandlerSwitcher
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer returns an initialized Server.
|
// NewServer returns an initialized Server.
|
||||||
|
@ -82,7 +82,7 @@ func (server *Server) Start() {
|
||||||
// Stop stops the server
|
// Stop stops the server
|
||||||
func (server *Server) Stop() {
|
func (server *Server) Stop() {
|
||||||
for _, serverEntryPoint := range server.serverEntryPoints {
|
for _, serverEntryPoint := range server.serverEntryPoints {
|
||||||
serverEntryPoint.httpServer.Close()
|
serverEntryPoint.httpServer.BlockingClose()
|
||||||
}
|
}
|
||||||
server.stopChan <- true
|
server.stopChan <- true
|
||||||
}
|
}
|
||||||
|
@ -142,22 +142,23 @@ func (server *Server) listenConfigurations() {
|
||||||
server.serverLock.Lock()
|
server.serverLock.Lock()
|
||||||
for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints {
|
for newServerEntryPointName, newServerEntryPoint := range newServerEntryPoints {
|
||||||
currentServerEntryPoint := server.serverEntryPoints[newServerEntryPointName]
|
currentServerEntryPoint := server.serverEntryPoints[newServerEntryPointName]
|
||||||
server.currentConfigurations = newConfigurations
|
if currentServerEntryPoint.httpServer == nil {
|
||||||
currentServerEntryPoint.httpRouter = newServerEntryPoint.httpRouter
|
newsrv, err := server.prepareServer(newServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], nil, server.loggerMiddleware, metrics)
|
||||||
oldServer := currentServerEntryPoint.httpServer
|
|
||||||
newsrv, err := server.prepareServer(currentServerEntryPoint.httpRouter, server.globalConfiguration.EntryPoints[newServerEntryPointName], oldServer, server.loggerMiddleware, metrics)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Error preparing server: ", err)
|
log.Fatal("Error preparing server: ", err)
|
||||||
}
|
}
|
||||||
go server.startServer(newsrv, server.globalConfiguration)
|
go server.startServer(newsrv, server.globalConfiguration)
|
||||||
currentServerEntryPoint.httpServer = newsrv
|
currentServerEntryPoint.httpServer = newsrv
|
||||||
|
currentServerEntryPoint.httpRouter = newServerEntryPoint.httpRouter
|
||||||
server.serverEntryPoints[newServerEntryPointName] = currentServerEntryPoint
|
server.serverEntryPoints[newServerEntryPointName] = currentServerEntryPoint
|
||||||
time.Sleep(1 * time.Second)
|
log.Infof("Created new Handler: %p", newServerEntryPoint.httpRouter.GetHandler())
|
||||||
if oldServer != nil {
|
} else {
|
||||||
log.Info("Stopping old server")
|
handlerSwitcher := currentServerEntryPoint.httpRouter
|
||||||
oldServer.Close()
|
handlerSwitcher.UpdateHandler(newServerEntryPoint.httpRouter.GetHandler())
|
||||||
|
log.Infof("Created new Handler: %p", newServerEntryPoint.httpRouter.GetHandler())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
server.currentConfigurations = newConfigurations
|
||||||
server.serverLock.Unlock()
|
server.serverLock.Unlock()
|
||||||
} else {
|
} else {
|
||||||
log.Error("Error loading new configuration, aborted ", err)
|
log.Error("Error loading new configuration, aborted ", err)
|
||||||
|
@ -264,7 +265,7 @@ func (server *Server) startServer(srv *manners.GracefulServer, globalConfigurati
|
||||||
log.Info("Server stopped")
|
log.Info("Server stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) prepareServer(router *mux.Router, entryPoint *EntryPoint, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) {
|
func (server *Server) prepareServer(router http.Handler, entryPoint *EntryPoint, oldServer *manners.GracefulServer, middlewares ...negroni.Handler) (*manners.GracefulServer, error) {
|
||||||
log.Info("Preparing server")
|
log.Info("Preparing server")
|
||||||
// middlewares
|
// middlewares
|
||||||
var negroni = negroni.New()
|
var negroni = negroni.New()
|
||||||
|
@ -303,7 +304,7 @@ func (server *Server) buildEntryPoints(globalConfiguration GlobalConfiguration)
|
||||||
for entryPointName := range globalConfiguration.EntryPoints {
|
for entryPointName := range globalConfiguration.EntryPoints {
|
||||||
router := server.buildDefaultHTTPRouter()
|
router := server.buildDefaultHTTPRouter()
|
||||||
serverEntryPoints[entryPointName] = serverEntryPoint{
|
serverEntryPoints[entryPointName] = serverEntryPoint{
|
||||||
httpRouter: router,
|
httpRouter: middlewares.NewHandlerSwitcher(router),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return serverEntryPoints
|
return serverEntryPoints
|
||||||
|
@ -332,7 +333,7 @@ func (server *Server) loadConfig(configurations configs, globalConfiguration Glo
|
||||||
if _, ok := serverEntryPoints[entryPointName]; !ok {
|
if _, ok := serverEntryPoints[entryPointName]; !ok {
|
||||||
return nil, errors.New("Undefined entrypoint: " + entryPointName)
|
return nil, errors.New("Undefined entrypoint: " + entryPointName)
|
||||||
}
|
}
|
||||||
newRoute := serverEntryPoints[entryPointName].httpRouter.NewRoute().Name(frontendName)
|
newRoute := serverEntryPoints[entryPointName].httpRouter.GetHandler().NewRoute().Name(frontendName)
|
||||||
for routeName, route := range frontend.Routes {
|
for routeName, route := range frontend.Routes {
|
||||||
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value)
|
||||||
route, err := getRoute(newRoute, route.Rule, route.Value)
|
route, err := getRoute(newRoute, route.Rule, route.Value)
|
||||||
|
|
Loading…
Reference in a new issue