网站首页 > 知识剖析 正文
httprouter是非常高效的http路由框架,gin框架的路由也是基于此库
地址:
https://github.com/julienschmidt/httprouter
一、使用方法
使用方法也比较简单,如下:
package main
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))
}
二、前缀树
这里在前文已经描述过了,详情看beego框架的路由
三、具体实现
1、Router
type Router struct {
trees map[string]*node // 存储了http不同方法的根节点
paramsPool sync.Pool
maxParams uint16
SaveMatchedRoutePath bool
// 是否通过重定向,给路径自添加/
RedirectTrailingSlash bool
// 是否通过重定向,自动修复路径,比如双斜杠等自动修复为单斜杠
RedirectFixedPath bool
// 是否检测当前请求的方法被允许
HandleMethodNotAllowed bool
// 是否自定答复OPTION请求
HandleOPTIONS bool
GlobalOPTIONS http.Handler
globalAllowed string
// 404处理
NotFound http.Handler
// 不被允许的方法处理
MethodNotAllowed http.Handler
// 异常处理
PanicHandler func(http.ResponseWriter, *http.Request, interface{})
}
这里的trees同样是存储了方法树,每一个不同的方法都有一个前缀树。
这里的httproute性能比beego高的一个原因是它使用的sync.Pool来优化内存的使用。
2、路由注册
在调用Handle方法的时候插入路由到路由树中。
func (r *Router) Handle(method, path string, handle Handle) {
varsCount := uint16(0)
if method == "" {
panic("method must not be empty")
}
if len(path) < 1 || path[0] != '/' {
panic("path must begin with '/' in path '" + path + "'")
}
if handle == nil {
panic("handle must not be nil")
}
if r.trees == nil {
r.trees = make(map[string]*node)
}
root := r.trees[method]
if root == nil {
root = new(node)
r.trees[method] = root
r.globalAllowed = r.allowed("*", "")
}
// 添加路由到前缀树中
root.addRoute(path, handle)
// Lazy-init paramsPool alloc func
// 初始化pool
if r.paramsPool.New == nil && r.maxParams > 0 {
r.paramsPool.New = func() interface{} {
ps := make(Params, 0, r.maxParams)
return &ps
}
}
}
3、插入算法
这里插入路由树的方式和beego不同,beego是按照路由/来分割的,比如/user和/userinfo这是对应的两个子树,而httprouter将相同前缀合并,/user是父节点。
下面给个例子
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>
同样,节点被区分了几种类型
type nodeType uint8
const (
static nodeType = iota // default 普通节点
root // 根节点
param // 参数节点
catchAll // 通配符
)
而节点的数据结构为
type node struct {
path string // 节点对应的路径
indices string
wildChild bool // 是否是通配符
nType nodeType // 节点类型
priority uint32
children []*node // 子节点
handle Handle
}
4、查找路由
httprouter是实现了net/http的ServeHTTP方法
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
}
// 重定向逻辑
}
// 异常处理逻辑
}
这里接受到http请求,根据请求的方法和路由信息,查找路由树。
然后根据路由和路由树进行对比,获取到执行的方法。
这一部分比beego的处理稍显简洁。
四、总结
httprouter使用的路由树比beego的路由树更加高效
1、对前缀树再优化,减少了重复的前缀。
2、对路由上的参数获取使用sync.Pool方式接受,减少了内存的分配。
猜你喜欢
- 2025-09-15 重磅好文透彻理解,异构图上 Node 分类理论与DGL源码实战
- 2025-09-15 亲测:把家里旧电脑改成服务器节点,每月省 300 块,操作超简单
- 2025-09-15 Kubernetes 25 大高频雷区与修复方案
- 2025-09-15 Vue3基础难点总结_vue3 从入门到实战
- 2025-09-15 双线程前端框架:Voe.js_前端多线程开发
- 2025-09-15 Vpp——node节点调度总结_node节点类型
- 2025-09-15 双向链表_双向链表和二叉链表
- 2025-09-15 Gin源码分析 - HttpRouter路由原理
- 2025-09-15 freeswitch的ACL规则_freeswitch api帮助文档
- 2025-09-15 深入拆解 Java HashMap:从数据插入到扩容的完整技术流程
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)