背景 
gin框架采用了httprouter进行路由匹配,所以废话少说,这篇文章来帮你了解他的源码实现
 
树 httprouter的路由匹配功能是基于radix tree(前缀树/基数树)这种数据结构实现的: 有读者可能会有疑问:为什么不按照RUL分隔符切割的来分割,每个segment作为一个节点,而是要用公共前缀来分割?针对这个疑问,了解树的开发者都知道,树的高度是影响查找效率的重要因素,高度越高,查找效率越低。两张图片来对比一下:
源码 demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import  (    "fmt"      "net/http"      "log"      "github.com/julienschmidt/httprouter"  ) func  Index (w http.ResponseWriter, r *http.Request, _ httprouter.Params)   {    fmt.Fprint(w, "Welcome!\n" ) } func  Hello (w http.ResponseWriter, r *http.Request, ps httprouter.Params)   {    fmt.Fprintf(w, "hello, %s!\n" , ps.ByName("name" )) } func  main ()   {    router := httprouter.New()     router.GET("/" , Index)     router.GET("/hello/:name" , Hello)     log.Fatal(http.ListenAndServe(":8080" , router)) } 
 
首先看到 httprouter.New(),因为它返回一个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 func  New ()  *Router   {	return  &Router{ 		RedirectTrailingSlash:  true , 		RedirectFixedPath:      true , 		HandleMethodNotAllowed: true , 		HandleOPTIONS:          true , 	} } type  Router struct  {	trees map [string ]*node 	paramsPool sync.Pool 	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 {})  } 
 
它实现了 ServeHTTP 这个函数,因此符合 net/http.Handler 接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func  (r *Router)  ServeHTTP (w http.ResponseWriter, req *http.Request)   {	if  r.PanicHandler != nil  { 		defer  r.recv(w, req) 	} 	path := req.URL.Path 	if  root := r.trees[req.Method]; root != nil  { 		if  handle, ps, tsr := root.getValue(path, r.getParams); handle != nil  { 			if  ps != nil  { 				handle(w, req, *ps) 				r.putParams(ps) 			} else  { 				handle(w, req, nil ) 			} 			return      ... } 
 
这就是处理请求的时候,查找路由树及handler的那部分,root.getValue就是查找路由树的具体函数,不过细节此处不表。
我们接下来看看注册路由的那部分:
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 func  (r *Router)  GET (path string , handle Handle)   {	r.Handle(http.MethodGet, path, handle) } func  (n *node)  addRoute (path string , handle Handle)   {	fullPath := path 	n.priority++ 	 	if  len (n.path) == 0  && len (n.indices) == 0  { 		n.insertChild(path, fullPath, handle) 		n.nType = root 		return  	} walk: 	for  {     ... } 
 
而 addRoute 函数就是实现radix tree这个数据结构的函数了,它会先找到共同的部分,然后考虑是否把路由切分为字节点,最后 把handler写上去(调用 insertChild 函数)。
1 2 3 4 5 6 7 8 9 type  node struct  {	path      string   	indices   string   	wildChild bool   	nType     nodeType  	priority  uint32   	children  []*node  	handle    Handle  } 
 
node就是保存这些资料的radix tree的节点