Go的框架很多,gin是其中一个,通过研究gin的源代码,来了解gin的实现原理,以及gin的使用方法。
Gin整体流程
gin.Default
gin.New
e.Get
e.Run
gin为包名,e为实例名
1.首先了解Engine与RouterGroup 以下是Engine的结构体定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 type Engine struct { RouterGroup RedirectTrailingSlash bool RedirectFixedPath bool HandleMethodNotAllowed bool ForwardedByClientIP bool RemoteIPHeaders []string TrustedPlatform string MaxMultipartMemory int64 RemoveExtraSlash bool delims render.Delims secureJSONPrefix string HTMLRender render.HTMLRender FuncMap template.FuncMap allNoRoute HandlersChain allNoMethod HandlersChain noRoute HandlersChain noMethod HandlersChain pool sync.Pool trees methodTrees maxParams uint16 maxSections uint16 trustedProxies []string trustedCIDRs []*net.IPNet }
关键属性为RouterGroup,RouterGroup是一个结构体,定义如下:
1 2 3 4 5 6 type RouterGroup struct { Handlers HandlersChain basePath string engine *Engine root bool }
通过这两个关键结构体,来对gin进行全局设置
2.了解gin.Default与gin.New 由gin.Default方法可知:
1 2 3 4 5 6 func Default () *Engine { debugPrintWARNINGDefault() engine := New() engine.Use(Logger(), Recovery()) return engine }
gin.Default方法中,调用了gin.New方法,而gin.New对整体的Engine进行了初始化.
engine.Use(Logger(), Recovery()),把Logger和Recovery中间件添加到了Engine中HandlersChain中
3.e.GET gin的注册方法都使用handle:
1 2 3 4 5 6 7 8 9 func (group *RouterGroup) POST(relativePath string , handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodPost, relativePath, handlers) }func (group *RouterGroup) GET(relativePath string , handlers ...HandlerFunc) IRoutes { return group.handle(http.MethodGet, relativePath, handlers) } ····
handler的代码如下:
1 2 3 4 5 6 func (group *RouterGroup) handle(httpMethod, relativePath string , handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() }
首先计算出绝对路径(基础路径+相对路径),然后合并当前的Handler,创建HandlersChain,添加当前注册的路由规则到路由树
浅看一下gin的路由树添加规则: gin给每一种请求方法都创建了一个路由树,每个路由树都是一个methodTree结构体 首先先找到路由树的根,判断这个根是否为空,如果为空,就创建一个根节点,然后将路由规则添加到根节点。
4.e.Run 1 2 3 4 5 6 7 8 func (engine *Engine) Run(addr ...string ) (err error ) { defer func () { debugPrintError(err) }() address := resolveAddress(addr) debugPrint("Listening and serving HTTP on %s\n" , address) err = http.ListenAndServe(address, engine) return }
此方法关键是使用了http.ListenAndServe 方法,将Engine作为参数传入,然后监听端口,等待请求。 由于Engine实现了ServeHTTP方法,所以可以将Engine作为参数传入ListenAndServe方法中。
1 2 3 4 5 6 7 8 9 10 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) }
ServeHTTP方法中,首先从sync.Pool中获取一个Context, 然后将ResponseWriter和Request赋值给Context, 然后调用handleHTTPRequest方法处理外部HTTP请求,最后将Context放回sync.Pool中。
总结 gin的使用方法很简单,通过gin.Default()或者gin.New()创建一个Engine,然后通过Engine的GET、POST等方法注册路由规则,最后通过Engine的Run方法监听端口,等待请求。 gin重写了net/http的route,通过ServeHTTP方法,将Engine作为参数传入ListenAndServe方法中,进行监听,通过自身的路由树匹配规则,来实现更快的响应。