gin路由匹配基于按http方法隔离的压缩radix树,时间复杂度o(k)且与路由总数无关;/user/:id与/user/profile共用/user节点但分支类型不同(param vs static),通过ntype和wildchild精确区分。

Gin 的路由匹配不是靠字符串切分或正则扫描,而是靠一棵按 HTTP 方法隔离、带压缩路径的 Radix 树实时跳转——只要路径段数固定,匹配耗时就基本恒定,跟注册多少条路由无关。
为什么 GET /user/:id 和 GET /user/profile 不冲突
两者在 Radix 树里共用 /user 节点,但后续分支不同:
-
/user/profile是静态子节点,nType为static,path字段存profile -
/user/:id是参数子节点,nType为param,paramName字段存id,且该节点被标记为wildChild = true - 匹配时先比对
/user,再根据下一个路径段是否满足参数规则(非/、非空)决定走哪条分支
addRoute() 注册时如何决定节点插入位置
核心逻辑在 node.addRoute(),不是简单追加,而是按「路径压缩」规则合并:
- 插入
/api/users和/api/posts时,/api/被提取为公共前缀节点,users和posts成为其两个子节点,indices字段值为"up" - 再插入
/api/users/:id,发现已有users子节点,就在此节点下新建一个param类型子节点,而非另起一棵树 - 如果插入
/api/user,而当前只有/api/users,会把users拆成user+s两层,实现路径压缩
通配符 *filepath 为什么必须放在最后
catchAll 节点是贪婪匹配,设计上强制它只能作为子节点列表的最后一个:
-
wildChild字段为true时,表示该节点下存在param或catchAll子节点,且该子节点必须排在children切片末尾 - 匹配逻辑会先尝试所有非通配子节点,失败后才 fallback 到最后一个
catchAll节点 - 若误写成
/static/*filepath/css,Gin 启动时会 panic,提示catchAll conflicts with existing children
不同 HTTP 方法为何各有一棵树
engine.trees 是 map[string]*node,key 是方法名,比如 GET、POST:
- 这样避免了方法判断和路由匹配耦合,请求进来先取
engine.trees[r.Method],再在这棵树上做路径查找 - 同一路径
/api/users注册了GET和POST处理器,它们分别落在两棵独立树上,互不干扰 - 没有注册的 Method 对应的树为空,
root == nil,直接返回 405
真正容易被忽略的是 priority 字段——它不控制执行顺序,只影响子节点排列:高频路径会被前置,减少遍历次数;但这个优先级是在注册时静态计算的,运行时不会动态调整。











