Merge branch 'contextCaching' into develop

This commit is contained in:
Manu Mtz-Almeida 2014-07-03 16:35:41 +02:00
commit bd33a76426

77
gin.go
View File

@ -31,6 +31,11 @@ type (
ErrorMsgs []ErrorMsg ErrorMsgs []ErrorMsg
Config struct {
CacheSize int
Preallocated int
}
// Context is the most important part of gin. It allows us to pass variables between middleware, // Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example. // manage the flow, validate the JSON of a request and render a JSON response for example.
Context struct { Context struct {
@ -39,8 +44,8 @@ type (
Keys map[string]interface{} Keys map[string]interface{}
Errors ErrorMsgs Errors ErrorMsgs
Params httprouter.Params Params httprouter.Params
Engine *Engine
handlers []HandlerFunc handlers []HandlerFunc
engine *Engine
index int8 index int8
} }
@ -56,9 +61,10 @@ type (
// Represents the web framework, it wrappers the blazing fast httprouter multiplexer and a list of global middlewares. // Represents the web framework, it wrappers the blazing fast httprouter multiplexer and a list of global middlewares.
Engine struct { Engine struct {
*RouterGroup *RouterGroup
HTMLTemplates *template.Template
cache chan *Context
handlers404 []HandlerFunc handlers404 []HandlerFunc
router *httprouter.Router router *httprouter.Router
HTMLTemplates *template.Template
} }
) )
@ -71,16 +77,35 @@ func (a ErrorMsgs) String() string {
return buffer.String() return buffer.String()
} }
// Returns a new blank Engine instance without any middleware attached. func NewWithConfig(config Config) *Engine {
// The most basic configuration if config.CacheSize < 2 {
func New() *Engine { panic("CacheSize must be at least 2")
}
if config.Preallocated > config.CacheSize {
panic("Preallocated must be less or equal to CacheSize")
}
engine := &Engine{} engine := &Engine{}
engine.RouterGroup = &RouterGroup{nil, "/", nil, engine} engine.RouterGroup = &RouterGroup{nil, "/", nil, engine}
engine.router = httprouter.New() engine.router = httprouter.New()
engine.router.NotFound = engine.handle404 engine.router.NotFound = engine.handle404
engine.cache = make(chan *Context, config.CacheSize)
// Fill it with empty contexts
for i := 0; i < config.Preallocated; i++ {
engine.cache <- &Context{Engine: engine}
}
return engine return engine
} }
// Returns a new blank Engine instance without any middleware attached.
// The most basic configuration
func New() *Engine {
return NewWithConfig(Config{
CacheSize: 1024,
Preallocated: 512,
})
}
// Returns a Engine instance with the Logger and Recovery already attached. // Returns a Engine instance with the Logger and Recovery already attached.
func Default() *Engine { func Default() *Engine {
engine := New() engine := New()
@ -97,6 +122,10 @@ func (engine *Engine) NotFound404(handlers ...HandlerFunc) {
engine.handlers404 = handlers engine.handlers404 = handlers
} }
func (engine *Engine) CacheStress() float32 {
return 1.0 - float32(len(engine.cache))/float32(cap(engine.cache))
}
func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) { func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) {
handlers := engine.combineHandlers(engine.handlers404) handlers := engine.combineHandlers(engine.handlers404)
c := engine.createContext(w, req, nil, handlers) c := engine.createContext(w, req, nil, handlers)
@ -107,6 +136,7 @@ func (engine *Engine) handle404(w http.ResponseWriter, req *http.Request) {
} }
c.Next() c.Next()
engine.reuseContext(c)
} }
// ServeFiles serves files from the given file system root. // ServeFiles serves files from the given file system root.
@ -138,14 +168,32 @@ func (engine *Engine) Run(addr string) {
/********** ROUTES GROUPING *********/ /********** ROUTES GROUPING *********/
/************************************/ /************************************/
func (group *RouterGroup) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context { func (engine *Engine) createContext(w http.ResponseWriter, req *http.Request, params httprouter.Params, handlers []HandlerFunc) *Context {
select {
case c := <-engine.cache:
c.Writer = w
c.Req = req
c.Params = params
c.handlers = handlers
c.Keys = nil
c.index = -1
return c
default:
return &Context{ return &Context{
Writer: w, Writer: w,
Req: req, Req: req,
index: -1,
engine: group.engine,
Params: params, Params: params,
handlers: handlers, handlers: handlers,
index: -1,
Engine: engine,
}
}
}
func (engine *Engine) reuseContext(c *Context) {
select {
case engine.cache <- c:
default:
} }
} }
@ -180,7 +228,9 @@ func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
p = path.Join(group.prefix, p) p = path.Join(group.prefix, p)
handlers = group.combineHandlers(handlers) handlers = group.combineHandlers(handlers)
group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) { group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
group.createContext(w, req, params, handlers).Next() c := group.engine.createContext(w, req, params, handlers)
c.Next()
group.engine.reuseContext(c)
}) })
} }
@ -221,6 +271,13 @@ func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc
/****** FLOW AND ERROR MANAGEMENT****/ /****** FLOW AND ERROR MANAGEMENT****/
/************************************/ /************************************/
func (c *Context) Copy() *Context {
var cp Context = *c
cp.index = AbortIndex
cp.handlers = nil
return &cp
}
// Next should be used only in the middlewares. // Next should be used only in the middlewares.
// It executes the pending handlers in the chain inside the calling handler. // It executes the pending handlers in the chain inside the calling handler.
// See example in github. // See example in github.
@ -358,7 +415,7 @@ func (c *Context) HTML(code int, name string, data interface{}) {
if code >= 0 { if code >= 0 {
c.Writer.WriteHeader(code) c.Writer.WriteHeader(code)
} }
if err := c.engine.HTMLTemplates.ExecuteTemplate(c.Writer, name, data); err != nil { if err := c.Engine.HTMLTemplates.ExecuteTemplate(c.Writer, name, data); err != nil {
c.Error(err, map[string]interface{}{ c.Error(err, map[string]interface{}{
"name": name, "name": name,
"data": data, "data": data,