introduce
- Gin implements the most important routing module based on Httprouter and uses a data structure similar to dictionary tree to store the mapping of routes to handle methods. It is also the reason for the high performance of the framework. Interested students can consult by themselves
- This article provides online mind mapping with articles to see more with less
Engine
Container objects, the basis of the entire frameworkEngine.trees
Is responsible for storing route and handle method mapping, using dictionary tree structureEngine.RouterGroup
Handlers store all middlewareContext
Context object, responsible for processingRequests and responses
, in which thehandlers
It stores the middleware and processing methods used to process the request
Initialize the container
Instantiate Engine by calling the gin.New() method. There are many arguments, but we only need to note RouterGroup,trees, and engine.pool.new
engine.pool.New
Responsible for creatingContext
Object, usingsync.Pool
Reduce resource consumption due to frequent context instantiations,
Handlers: func New() *Engine {debugPrintWARNINGNew() Engine := &engine { RouterGroup{ Handlers: nil, basePath: "/", root: true, }, FuncMap: template.FuncMap{}, RedirectTrailingSlash: true, RedirectFixedPath: false, HandleMethodNotAllowed: false, ForwardedByClientIP: true, AppEngine: defaultAppEngine, UseRawPath: false, RemoveExtraSlash: false, UnescapePathValues: true, MaxMultipartMemory: DefaultMultipartMemory, //trees is the most important point!!!! Trees: make(methodTrees, 0, 9), delims: render.Delims{Left: "{{", Right: "}}"}, secureJsonPrefix: "while(1);" . } engine. RouterGroup. Engine = engine / / the context is realized by using the sync/pool pool here, reduce the consumption of resources caused by frequent context instantiation engine. The pool. The New = func () interface{} { return engine.allocateContext() } return engine }Copy the code
Registered middleware
Gin’s high performance is mainly dependent on trees. The contents of each node can be thought of as a dictionary tree of key->value. Key is a route and value is a []HandlerFunc, which stores the sequential execution of middleware and Handle controller methods.
Register global middleware
Gin.Use() calls RouterGroup. use() to write to RouterGroup.Handlers
func (engine *Engine) Use(middleware ... HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers() // Register the 404 handler
engine.rebuild405Handlers() // Register the 405 processing method
return engine
}
The 'Handlers' field is an array used to store middleware
func (group *RouterGroup) Use(middleware ... HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
Copy the code
Register routing group middleware
- through
Group()
Method returns a newly generatedRouterGroup
Pointer used to separate each routing group from the middleware loaded differently- Notice here
Handlers: group.combineHandlers(handlers)
This line of code copies a copy of the global middleware to the newly generatedRouterGroup.Handlers
, and then the route can be written to the tree node during registration
group := g.Group("/test_group")
group.Use(middleware.Test())
{
// The final route is written to the tree node along with the middleware and handle methods
group.GET("/test",handler.TestTool)
}
// Returns a RouterGroup pointer
func (group *RouterGroup) Group(relativePath string, handlers ... HandlerFunc) *RouterGroup {
return &RouterGroup{
// Copy a copy of the global middleware
Handlers: group.combineHandlers(handlers),
basePath: group.calculateAbsolutePath(relativePath),
engine: group.engine,
}
}
Copy the code
Register routing and middleware
Either request will eventually call RouterGroup.handle, which has two main functions
Handles the format of routes by spelling them into ‘/’ characters
Handlers make a copy of the RouterGroup.Handlers make a list with the corresponding handle for the route and put it in the tree
Finally, call trees. AddRoute to add a node
g.GET("/test_tool", middleware.Test(),handler.TestTool)
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
// The root directory and the route are combined to spell the route as a route beginning with the '/' character
absolutePath := group.calculateAbsolutePath(relativePath)
Handlers make a list of routerGroups. Handlers and put them in the tree
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
// Call 'trees' to add nodes
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
assert1(path[0] = ='/'."path must begin with '/'") assert1(method ! =""."HTTP method can not be empty")
assert1(len(handlers) > 0."there must be at least one handler")
debugPrintRoute(method, path, handlers)
root := engine.trees.get(method)
if root == nil {
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers)
}
Copy the code
Start the
The service is started by calling net/ HTTP, and since Engine implements the ServeHTTP method, you just need to pass the Engine object directly to initialize and start it
g.Run()
func (engine *Engine) Run(addr ...string) (err error) {
defer func(a) { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine)
return
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
// An interface from the net/ HTTP definition, which can be used as a function to handle requests as long as it is implemented
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// Implements the ServeHTTP method
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
Copy the code
Handle the request
- Just be careful here
handleHTTPRequest(c *Context)
The method is good- Through the request method and route to find the corresponding tree node, obtain the stored
[]HandlerFunc
List, by callingc.Next()
Handle the request- By moving the subscript recursively, the final result is returned
func (engine *Engine) handleHTTPRequest(c *Context) { ... // t := engine.trees for i, tl := 0, len(t); i < tl; i++ { ... // Find route in tree value := root.getValue(rPath, c.Params, unescape) if value.handlers ! = nil { c.handlers = value.handlers c.Params = value.params c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() return } ... }... } func (c *Context) Next() {c.index++ for C.index < int8(len(c.handlers)) { c.handlers[c.index](c) c.index++ } }Copy the code
feeling
- Gin framework source code is relatively easy to understand, this is precisely its advantage, Golang language itself is relatively mature, the framework is only a convenient scaffolding for you to do the project, you can completely according to your needs to customize your own exclusive
gin
Framework, including logging, caching, queues, and so on - The core is routing storage tree, learn algorithm, data structure is the key
Reference documentation
Gin Chinese document https://gin-gonic.com/zh-cn/docs/introduction/
The gin project is at https://github.com/gin-gonic/gin
httprouter
https://github.com/julienschmidt/httprouter