注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
在上一篇文章当中阅读了 Go 语言的一个高性能的 Web 框架 Gin,Web 框架当中最重要的功能之一是路由,Gin 的路由就是由 httprouter 这个包实现的
地址
特性
- 基于基数树实现的高性能路由框架
- 仅支持精确匹配
- 不必关心 URL 结尾的斜线
- 路径自动校正,例如在 url 路径当中有
../
,//
的时候 - 可以在 URL 当中设置参数,例如
/user/:id
- 零内存分配
- 不存在服务器崩溃,可以通过设置
panic handler
使服务器从 panic 当中恢复 - 适合 API 构建
源码
两个问题
解决两个问题,就基本明白了这个路由框架
- 路由是是如何注册?如何保存的?
- 当请求到来之后,路由是如何匹配,如何查找的?
一个 Demo
还是从一个Hello World
讲起
1 2 3 4 5 6 7
| func main() { r := httprouter.New() r.GET("/:name", func(writer http.ResponseWriter, request *http.Request, params httprouter.Params) { fmt.Fprintf(writer, "hello, %s!\n", params.ByName("name")) }) http.ListenAndServe(":8080",r) }
|
httprouter.New()
初始化了一个 Router,下面直接看一下 Router 的结构
Router
在 Router 的源码当中有十分详尽的注释,这里按照我个人的理解注释一下
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
|
type Router struct { trees map[string]*node
RedirectTrailingSlash bool
RedirectFixedPath bool
HandleMethodNotAllowed bool
HandleOPTIONS bool
NotFound http.Handler
MethodNotAllowed http.Handler
PanicHandler func(http.ResponseWriter, *http.Request, interface{}) }
|
初始化 Router 之后看看路由是如何保存并且注册的
路由是如何保存的?
这里以官方 Readme 当中的例子说明:
如果注册了以下路由
1 2 3 4 5 6 7 8
| r.GET("/", f1) r.GET("/search/", f2) r.GET("/support/", f3) r.GET("/blog/", f4) r.GET("/blog/:post/", f5) r.GET("/about_us/", f6) r.GET("/about_us/team/", f7) r.GET("/contact/", f8)
|
那么这些路由会如下方所示,以一颗树的形式保存,并且这些路由的公共前缀会被抽离并且变为上一层节点
Priority 表示加上自身一共有多少个节点
Path 表示路径
Handle 表示路由注册的方法
1 2 3 4 5 6 7 8 9 10 11
| Priority Path Handle 9 \ *<1> 3 ├s nil 2 |├earch\ *<2> 1 |└upport\ *<3> 2 ├blog\ *<4> 1 | └:post nil 1 | └\ *<5> 2 ├about-us\ *<6> 1 | └team\ *<7> 1 └contact\ *<8>
|
r.Handle
r.Get
, r.Post
等方法实质都是通过调用 r.Handle 实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func (r *Router) Handle(method, path string, handle Handle) { if path[0] != '/' { panic("path must begin with '/' in path '" + path + "'") }
if r.trees == nil { r.trees = make(map[string]*node) }
root := r.trees[method] if root == nil { root = new(node) r.trees[method] = root }
root.addRoute(path, handle) }
|
node
路由是注册到一颗路由树当中的,先看看节点的源码,再来分析,是如何添加路由的
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
| type node struct { path string
wildChild bool
nType nodeType
maxParams uint8
indices string
children []*node
handle Handle
priority uint32 }
|
路由树是如何生成的?
未完待续
关注我获取更新
猜你喜欢