4

Golang httprouter 库分析

 2 years ago
source link: https://pandaychen.github.io/2021/10/01/GOLANG-HTTPROUTER-ANALYSIS/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

0x00 前言

httprouter 非常高效的一个 http 路由框架,gin 框架的路由也基于此库。使用比较简单:

func main() {
    router := httprouter.New()

    //注册路由
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)

    log.Fatal(http.ListenAndServe(":8080", router))
}

0x01 基数树 Radix Tree

httprouter 基于 Radix Tree 实现,Radix Tree 的特点是拥有共同前缀的节点也共享同一个父节点。经典例子:

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>

对应于下面的 GET 请求,*<num> 是方法(handler)对应的指针,从根节点遍历到叶子节点我们就能得到完整的路由表:

GET("/", func1)
GET("/search/", func2)
GET("/support/", func3)
GET("/blog/", func4)
GET("/blog/:post/", func5)
GET("/about-us/", func6)
GET("/about-us/team/", func7)
GET("/contact/", func8)

0x02 主要数据结构

Router

// Router is a http.Handler which can be used to dispatch requests to different
// handler functions via configurable routes
type Router struct {
    trees map[string]*node  //Map 存储了 HTTP 不同方法的根节点
    paramsPool sync.Pool    //WITH efficiency
    maxParams  uint16
    SaveMatchedRoutePath bool
    RedirectTrailingSlash bool
    RedirectFixedPath bool
    HandleMethodNotAllowed bool
    HandleOPTIONS bool
    GlobalOPTIONS http.Handler  //
    globalAllowed string
    NotFound http.Handler   //
    MethodNotAllowed http.Handler   //
    PanicHandler func(http.ResponseWriter, *http.Request, interface{})
}

Router 也实现了 ServeHTTP 方法,因此符合 net/http.Handler 接口,ServeHTTP 方法为处理请求时,查找路由树及 Handler 的逻辑:

// ServeHTTP makes the router implement the http.Handler interface.
func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    if r.PanicHandler != nil {
        defer r.recv(w, req)
    }

    path := req.URL.Path

    // 获取本次请求路由(path)的 handle 方法
    if root := r.trees[req.Method]; root != nil {
    	if handle, ps, tsr := root.getValue(path, r.getParams); handle != nil {
            //root.getValue 查找路由树的具体方法
    		if ps != nil {
    			handle(w, req, *ps)
    			r.putParams(ps)
    		} else {
    			handle(w, req, nil)
    		}
    		return
    .....
}

0x03 路由注册

0x04 路由查找

0x05 查找算法

0x06 参考


Related Issues not found

Please contact @pandaychen to initialize the comment


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK