自己根据go-zero单体框架实现的一个精简版框架https://github.com/wanmei002/go-zero-learn, 新手直接看go-zero 框架可能会绕,看这个好理解
找到实现 http.Handler 接口的结构体
顺着入口函数 server.Start()
,找到了 router.NewRouter()
创建的对象实现了 http.Handler
接口
创建的对象为 router.patRouter
结构体的实例
这个结构体有两个方法
- Handle 路由生成字典树
- ServeHTTP 方法实现了 http.Handler 接口,这个方法里有路由匹配,匹配失败的逻辑
type patRouter struct {
trees map[string]*search.Tree
notFound http.Handler
notAllowed http.Handler
}
路由生成字典树
原理图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wqkjcnbo-1620886851260)(router-tree.png)]
生成字典树的主要代码,完整代码请看go-zero/rest/router/patrouter.go
:
// Handle :
// method : GET POST
// reqPath : ep: /home/article
// handler :
func (pr *patRouter) Handle(method, reqPath string, handler http.Handler) error {
if !validMethod(method) {
return ErrInvalidMethod
}
if len(reqPath) == 0 || reqPath[0] != '/' {
return ErrInvalidPath
}
fmt.Println("reqPath before:", reqPath)
cleanPath := path.Clean(reqPath)
fmt.Println("cleanPath:", cleanPath)
tree, ok := pr.trees[method]
if ok {
err := tree.Add(cleanPath, handler)
return err
}
tree = search.NewTree()
pr.trees[method] = tree
fmt.Printf("pr: %+v\n", pr)
err := tree.Add(cleanPath, handler)
fmt.Printf("tree : %+v\n", tree)
return err
}
递归生成字典树:
func add(nd *node, route string, item interface{}) error {
if len(route) == 0 {
if nd.item != nil {
return ErrDupItem
}
nd.item = item
return nil
}
if route[0] == slash {
return ErrDupSlash
}
for i := range route {
if route[i] == slash {
token := route[:i]
children := nd.getChildren(token)
if child, ok := children[token]; ok {
if child != nil {
return add(child, route[i+1:], item)
}
return ErrInvalidState
}
child := newNode(nil)
children[token] = child
return add(child, route[i+1:], item)
}
}
children := nd.getChildren(route)
if child, ok := children[route]; ok {
if child.item != nil {
return ErrDupItem
}
child.item = item
} else {
children[route] = newNode(item)
}
return nil
}