gin的官方简介如下:
gin is a web framework written in Go (Golang). It features a martini-like API with performance that is up to 40 times faster thanks to httprouter. If you need performance and good productivity, you will love gin.
gin是一个用Go (Golang)编写的web框架。类似于一个Martini的API,由于使用了httprouter,性能提高了40倍。如果您需要良好的表现和工作效率,您会喜欢gin。
gin框架速度很快, 这也是在企业中被广泛使用的原因之一。gin之所以能够提供高效的路由解析,是因为使用了前缀树这样的数据结构来进行path和handler方法的映射,本篇文章就来探讨gin框架的路由模块。
1、Engine的实例化
Engine是容器对象,是整个框架的基础。gin提供了两个方法来创建Engine实例,New和Default方法。
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
//Default()方法也调用了New()方法来创建Engine实例
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
default方法除了包含New方法外,还增加了两个中间件,Logger是默认系统输出日志,Recovery是在报panic的时候恢复系统输出并返回500。
New()方法:
// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash: true
// - RedirectFixedPath: false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP: true
// - UseRawPath: false
// - UnescapePathValues: true
func New() *Engine {
debugPrintWARNINGNew()
engine := &Engine{
//RouterGroup是管理路由和中间件的组件,它定义了URL路径与处理函数的映射关系。
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
FuncMap: template.FuncMap{},
//RedirectTrailingSlash当前路径处理函数不存在时,是否允许重定向到路径+或-"/"的处理函数
RedirectTrailingSlash: true,
//RedirectFixedPath路由未命中时,是否允许修复当前处理路径(如对路径大小写不敏感查询、删除多余路径元素等)并重定向
RedirectFixedPath: false,
//HandleMethodNotAllowed路由未命中时...这个我也暂时不明白啥意思
HandleMethodNotAllowed: false,
//ForwardedByClientIP是否转发客户端ip
ForwardedByClientIP: true,
//AppEngine如果启用该引擎,将插入一些以”X-AppEngine..."开头的标题,以便更好地与PaaS集成
AppEngine: defaultAppEngine,
//UseRawPath是否使用未转义的请求路径,即url.RawPath,默认为false。如果为true,url.RawPath将用于查找参数。
UseRawPath: false,
RemoveExtraSlash: false,
//UnescapePathValues
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
engine.pool.New = func() interface{} {
return engine.allocateContext()
}
return engine
}
创建的Engine实例,与路由匹配相关的有RouterGroup、trees、engin.pool.New这三个参数.
RouterGroup结构体:
// RouterGroup is used internally to configure router, a RouterGroup is associated with
// a prefix and an array of handlers (middleware).
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
Handler为中间件数组,Handler的类型为HandlersChain,是方法类型HandlerFunc的数组。HandlerFunc是参数为*Context的回调函数。
// HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context)
// HandlersChain defines a HandlerFunc array.
type HandlersChain []HandlerFunc
注:Handler、HandlerFunc、Handle、HandlerFunc的区别:
//Handler
type Handler interface{
ServeHTTP(ResponseWriter, *Request)
}
//HandlerFunc
type HandlerFunc func(ResponseWriter, *Request)
//Handler, 一个是方法,一个是函数 (方法是包含了接收者的函数)
func (mux *ServeMux) Handler(pattern string, handler Handler)
func Handler(pattern string, handler Handler)
//HandlerFunc,与Handler同理,一个是方法,一个是函数
func (mux *ServeMux) HandlerFunc(pattern string, callback HandlerFunc)
func HandlerFunc(pattern string, callback HandlerFunc)
Handler : Handler是一个接口,实现了ServeHTTP方法,这个ServeHHTTP方法其实就是一个回调函数,用于处理http请求。
HandlerFunc: HandlerFunc是一个回调函数类型的别名,等价于Handler的ServeHTTP方法。
Handle:用于给我们的WEB程序注册路由,传入的第二个参数是一个Handler。
HandleFunc: 与Handle一样,只不过传入的第二个参数是一个HandlerFunc。
Handle和HandleFunc基本是等价的,它们都代表了如何处理一个http请求,拿到一个Handler以及HandlerFunc,只是在使用上略微不同。
trees是最重要的一部分,负责路由和handler方法的映射,因为它gin框架才实现了高性能的路由匹配。
结构体类似一个key-value类型的字典树,key为字符串格式的路由,value为[]HandlerFunc,这个方法数组存储的是按顺序的中间件和handler方法。
第一部分中,gin框架创建的Engine实例,tree是make(methodTrees, 0, 9),make方法创建的切片,初始长度为0,预留总长度为9.
methodTrees和methodTree结构:
type methodTree struct {
method string
root *node
}
type methodTrees []methodTree
methodTrees是[]methodTree,而methodTree是由string类型的method和node类型的指针组成。
method是HTTP的访问方法,Go中一共分为9中,也就是说methodTree数组最多有9棵树。
Go支持的的HTTP访问方法:
package http
// Common HTTP methods.
//
// Unless otherwise noted, these are defined in RFC 7231 section 4.3.
const (
MethodGet = "GET"
MethodHead = "HEAD"
MethodPost = "POST"
MethodPut = "PUT"
MethodPatch = "PATCH" // RFC 5789
MethodDelete = "DELETE"
MethodConnect = "CONNECT"
MethodOptions = "OPTIONS"
MethodTrace = "TRACE"
)
每一种方法对应一个node根节点
node的结构和类型:
type nodeType uint8
const (
static nodeType = iota // default
root
param
catchAll
)
type node struct {
//path是当前节点的相对路径
path string
//indices是目录,由所有子节点的path[0]组成的字符串
indices string
//children是所有的子节点
children []*node
//handlers是包括中间件的一系列当前节点的处理函数
handlers HandlersChain
//priority是当前节点及子孙节点的实际路由数量,即经过该节点有多少条路径,等同于前缀树中节点的path值(一个节点的子孙节点越多,权重越大,该节点在其父节点的indices目录中越靠前,在父节点的children数组中也越靠前)
priority uint32
//nType是当前节点的类型:
//nodeType分为四种类型,默认为static是普通类型节点,root是根节点,即methodTree结构体中与method一一对应的节点,param是参数路由类型的节点,即路由中可以夹带参数,例如/path/:id,:后到下一个/之前的内容会赋值给id,catchAll是匹配所有内容的路由,如/path/*id,*号后面所有的内容都会赋值给参数id,包括所有/。
nType nodeType
//maxParams是子孙节点的最大参数数量
maxParams uint8
//wildChild是一个bool值,表示孩子节点是否有通配符
wildChild bool
//fullPath是所有子孙节点路径的最长前缀
fullPath string
}