gin 路由注册分析
源码分析
重要结构体
Engine是gin的“引擎”, 负责整个路由的工作
type Engine struct {
//配置管理路由器
RouterGroup
...
FuncMap template.FuncMap
allNoRoute HandlersChain
allNoMethod HandlersChain
noRoute HandlersChain
noMethod HandlersChain
pool sync.Pool
//路由树, 存放url和handle
trees methodTrees
}
RouterGroup实现接口, 负责管理和配置路由
type IRoutes interface {
Use(...HandlerFunc) IRoutes
Handle(string, string, ...HandlerFunc) IRoutes
Any(string, ...HandlerFunc) IRoutes
GET(string, ...HandlerFunc) IRoutes
POST(string, ...HandlerFunc) IRoutes
DELETE(string, ...HandlerFunc) IRoutes
PATCH(string, ...HandlerFunc) IRoutes
PUT(string, ...HandlerFunc) IRoutes
OPTIONS(string, ...HandlerFunc) IRoutes
HEAD(string, ...HandlerFunc) IRoutes
StaticFile(string, string) IRoutes
Static(string, string) IRoutes
StaticFS(string, http.FileSystem) IRoutes
}
比如注意路由如下:
g.POST("/v1/push", controllers.PushMsg)
POST方法注册到路由, HandlerFunc接口 func(*gin.Context)
func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodPost, relativePath, handlers)
}
返回IRoutes, 返回接口, 是为了 多次链式调用
注册到路由树tree
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
//调整url路径, 若有group拼接
absolutePath := group.calculateAbsolutePath(relativePath)
//聚合所有handle(真正的响应handle在所有注册中间件后面), 链式handle最大层次62
handlers = group.combineHandlers(handlers)
//添加到gin engine 的路由树trees
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj() }
添加路由方法到路由树
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)
//获取or创建POST的根
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)
}
添加路由方法
func (n *node) addRoute(path string, handlers HandlersChain) {
fullPath := path
n.priority++
numParams := countParams(path)
//空树,直接插入子节点
if len(n.path) == 0 && len(n.children) == 0 {
n.insertChild(numParams, path, fullPath, handlers)
n.nType = root
return
}
walk:
...
//路由树的构建
//最长公共子串, 子节点, 插入...
}
总结
Go/src/net/http/server.go:1760
func (c *conn) serve(ctx context.Context) {
...
//重点逻辑, 接口方式多态
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
基本所有的go web框架(gin, beego等)都是基于原生http包来构建的, 它们主要是自己实现了路由管理.
这其中的关键是, 实现http包的Handler, 然后传入自定义路由到此, http.ListenAndServe(":8080", g)
,
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请求到来时就会在serve()函数中根据是否自定义路由来选择自定义路由来做路由查找的逻辑处理