共计 1722 个字符,预计需要花费 5 分钟才能阅读完成。
Gin 路由主要流程实现
经过上一篇的学习笔记,我们已经知道了 Gin router 的主要流程。但是我们看到代码和方法体总体很长,其中大部分是参数路由的判断。这些零散的小逻辑,让我们阅读源码的时候更难理解了一些。但是其实基数树的逻辑兵没有这么的复杂,所以我们还是按照老规矩,自己实现以下这个简单的基数树值包含主流程。代码如下:
package mygin
import "fmt"
type Trees map[string]*node
type node struct {
path string
indices string
children []*node
handlers HandlerList
}
func (n *node) addRoute(path string, handlers HandlerList) {if len(n.path) > 0 || len(n.children) > 0 {
walk:
for {
// 找到相等的 index
i := 0
max := min(len(path), len(n.path))
for max > i && path[i] == n.path[i] {i++}
// 需要把原来的作为子 node 放到新 node 中
if i < len(n.path) {
// 新建 node
child := node{path: n.path[i:],
indices: n.indices,
handlers: n.handlers,
children: n.children,
}
n.children = []*node{&child}
n.indices = string([]byte{n.path[i]})
n.path = path[:i]
n.handlers = nil
}
// 判断子节点如果有相同开头的字符 则从新跳入循环
if i < len(path) {c := path[i]
for index := 0; index < len(n.indices); index++ {if c == n.indices[index] {n = n.children[index]
path = path[i:]
continue walk
}
}
// 把新请求的 path 加入到 router 中
n.insertChild(path[i:], path, handlers, i)
return
}
return
}
} else {
// 如果为空
n.path = path
n.handlers = handlers
}
}
func (n *node) insertChild(path, fullPath string, handlers HandlerList, index int) {child := node{}
child.handlers = handlers
child.indices = ""
child.path = path
n.indices += string([]byte{fullPath[index]})
n.children = append(n.children, &child)
}
func min(a, b int) int {
if a > b {return b}
return a
}
func (n *node) getValue(path string) (handlers HandlerList) {
index := 1
walk:
for {fmt.Println("loop num:", index)
if len(path) > len(n.path) {path = path[len(n.path):]
c := path[0]
for i := 0; i < len(n.indices); i++ {if c == n.indices[i] {n = n.children[i]
index++
goto walk
}
}
} else if len(path) == len(n.path) {
handlers = n.handlers
return
}
}
}
总结
上面的代码已经不需要太多的注释了,去掉了参数节点的代码整个流程已经很明确了。
结束语
Gin 的源码学习和分析已经全部结束了。其实对于 Gin 框架源码的分析已经有了很多文章,但是如果在学习的同时自己也简单的模仿和实现一下这些功能对于我们理解就更有帮助。
Gin 是一个十分轻巧 http 框架,代码也十分的简介和清楚。实现也有一些亮点,我觉得很适合新手对于 go 源码学习和分析的入门框架。希望这 5 篇文章能对在学习 go 中的人有一些帮助。
正文完