3. httprouter路由原理:
①. 路由器依赖于大量使用通用前缀的树结构,是一个紧凑的前缀树(或只是基数树).
②. 具有公共前缀的节点也共享一个公共的父节点.
(1). 举例 - Get树:
①. 路由:
router := httprouter.New()
router.GET("/search/", func1)
router.GET("/support/", func2)
router.GET("/blog/:post/", func3)
router.GET("/about-us/", func4)
router.GET("/about-us/team/", func5)
router.GET("/contact/", func6)
②. GET请求方法的路由树:
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>
③. priority优先级:
a. 层次越深,优先级priority越高.
b. 为了保证有序.
④. 前缀树:
a. 定义的路径是有限的,就可以按照字母的顺序做一个前缀树.
b. search和support有相同的s前缀,这就是前缀树.
c. s是没有处理函数的,所以是nil
⑤. blog/:post中:
a. :post是实际文章名称的占位符(参数).
b. 与hash-maps不同,这种树结构还允许使用像:post参数这种动态部分.
c. 实际上是根据路由模式进行匹配,而不仅仅是比较哈希值.
⑥. Handle:
a. 表示前缀树节点对应的Handle处理函数(指针)的内存地址.
b. 沿着树从根到叶的路径走,可以得到完整的路径.
(2). 前缀树的优点:
①. 处理每一个请求路径,如:"about-us".
②. 从前缀树从上向下匹配:
a. s不符合,往下.
b. blog不符合,往下.
c. about-us符合:
(1). 查看后面没有,正好匹配,找到Handle为*<b>对应的函数.
(2). 否则,继续匹配.
③. 其实就是字符串匹配.
④. 层次结构:
a. 由于URL路径具有层次结构.
b. 只使⽤有限的⼀组字符(字节值),可能会有许多常⻅的前缀.
c. 路由器为每个请求⽅法管理⼀个单独的树.
d. 它⽐在每个节点中保存⼀个⽅法(>句柄映射)更节省空间.
(3). 优先级是什么?
①. 考虑到可伸缩性,每个树级别上的子节点都按Priority(优先级)排序.
|---------
|--------
|-----
|--
|-
a. 当一个url,下面挂载的Handler越多,就优先去匹配它.
b. 值越大,说明大部分路由都在这个节点上,命中率会很高.
c. 这是一种成本补偿,最长可达路径(最高成本)总是可以先求值.
(1). 如果最上面那个节点url占用了80%.
(2). 从下往上匹配的话,就会有80%的url从下面向上匹配,做无用功.
(3). 反之,如果是从上往下匹配,下面的请求也是只有几%的在做无用功.
②. 优先级就是子节点(子节点、子子节点等等)中所有注册的句柄的数量.
③. 源码中:
a. 从上到下,从左到右计算节点的函数:addRoute、insertChild
b. 匹配路由函数:getValue
(4). 为什么层级越深的,优先级越高?
①. 寻找的方案:
a. 字符串匹配
b. hash maps:根据key找值、速度快
②. 为什么不用hash maps?
a. 因为url是一种有限的字符串集合.
b. hash的路由无法匹配包含blog\:post这种路由:
(1). :post只是文章名称的占位符(参数).
(2). :post是可变的、动态的任意参数.
c. 实际上是根据路由模式进行匹配,并不仅仅是比较哈希值.
d. hash maps格式比较固定.
4. 源码中使用_是什么作用?
// Make sure the Router conforms with the http.Handler interface
var _ http.Handler = New()
// New returns a new initialized Router.
// Path auto-correction, including trailing slashes, is enabled by default.
func New() *Router {
return &Router{
RedirectTrailingSlash: true,
RedirectFixedPath: true,
HandleMethodNotAllowed: true,
HandleOPTIONS: true,
}
}
(1). 说明:
①. http.Handler是一个接口.
②. var _ http.Handler = New()
a. var表示声明
b. _表示变量名
c. http.Handler表示类型
d. New()返回的是一个结构体指针
③. 作用:
a. 确保接口实现,确保变量满足了接口实现,并非自动初始化.
b. 相当于
var a http.Handler // 声明了一个变量,类型是http.Handler
a = New() // 赋值
c. 什么情况下,可以把这个New()的结构体指针赋值给a?
(1). 是不是只有当这个结构体实现了接口的所有方法的时候,表示这个结构体满足了这个接口.
(2). 就能把这个结构体变量直接赋值给接口变量,即a.
d. 这样做的,目的是确保New函数返回的结构体是可以满足http.Handler.
e. 但是这样写,a是没有使用的.所以,使用_来代替.