树袋熊|实现,前缀树算法实现路由匹配原理解析:Go
路由功能是web框架中一个很重要的功能 , 它将不同的请求转发给不同的函数(handler)处理 , 很容易能想到 , 我们可以用一个字典保存它们之间的对应关系 , 字典的key存放path , value存放handler 。 当一个请求过来后 , 使用routers.get(path,None)就可以找到对应的handler 。
利用字典实现路由可以参考我的这篇文章:动手实现web框架[1] 。
使用字典有一个问题 , 不支持动态路由 。 如果路由像这样呢?
/hello/:name/profilename前面是通配符: , 表示这是个动态的值 。 一个解决办法是使用前缀树trie 。
前缀树leetcode中有这个算法 , 点这里[2]查看 。
前缀树前缀树 , 首先是一棵树 。 不同的是树中一个节点的所有子孙都有相同的前缀 。 前缀树将单词中的每个字母依次插入树中 , 插入前首先确认该单词是否存在 , 不存在才创建新节点 , 如果一个单词已经全部插入 , 则将末尾单词设置为标志位 。
typeNodestruct{isWordbool//是否是单词结尾nextmap[string]*Node//子节点}typeTriestruct{root*Node}以单词leetcode , leetd和code为例 , 首先一次插入leetcode中的每个单词 , 然后插入leetd的时候 , leet在树中已经存在 , 跳过往下 , 现在要插入字母d , 不存在 , 所以新建节点插入树中 , 并将该节点的isWord置位true , 表明到了单词末尾 。
最终插入结果为:
func(this*Trie)Search(wordstring)bool{cur:=this.rootfor_,w:=range[]rune(word){c:=string(w)ifcur.next[c]==nil{returnfalse}cur=cur.next[c]}returncur.isWord}明白了前缀树的原理 , 我们来看看路由匹配是如何利用前缀树来实现的 。
路由前缀树go语言中gin框架的路由实现就是利用前缀树 , 可以看看它的源代码是如何实现的 。
考虑一下 , 路由或者说路径的特点 , 是以/分隔的单词组成的 , 那我们将/的每一部分挂靠在前缀树上就可以了 。 如下图所示:
还有一点需要考虑 , 我们在用web框架定义路由的时候 , 常见的做法是根据不同的HTTP方法来定义 。 比如:
//以go语言gin框架为例g:=gin.New()g.GET("/hello",Hello)g.POST("/form",Form)对于同一个路径 , 可能有多个方法支持 。 所以我们要以不同HTTP方法为树根创建前缀树 。 当一个GET请求过来的时候 , 就从GET树上搜索 , POST请求就从POST树上搜索 。
typenodestruct{pathstring//路由路径partstring//路由中由'/'分隔的部分childrenmap[string]*node//子节点isWildbool//是否是通配符节点}typerouterstruct{rootmap[string]*node//路由树根节点routemap[string]HandlerFunc//路由处理handler}依照上面的前缀树算法的实现 , 照葫芦画瓢 , 我们可以写出插入路由和搜索路由的方法:
//addRoute绑定路由到handlerfunc(r*router)addRoute(method,pathstring,handlerHandlerFunc){parts:=parsePath(path)if_,ok:=r.root[method];!ok{r.root[method]=&node{children:make(map[string]*node)}}root:=r.root[method]key:=method+"-"+path//将parts插入到路由树for_,part:=rangeparts{ifroot.children[part]==nil{root.children[part]=&node{part:part,children:make(map[string]*node),isWild:part[0]==':'||part[0]=='*'}}root=root.children[part]}root.path=path//绑定路由和handlerr.route[key]=handler}//getRoute获取路由树节点以及路由变量func(r*router)getRoute(method,pathstring)(node*node,paramsmap[string]string){params=map[string]string{}searchParts:=parsePath(path)//getmethodtrievarokboolifnode,ok=r.root[method];!ok{returnnil,nil}//在该方法的路由树上查找该路径fori,part:=rangesearchParts{vartempstring//查找child是否等于partfor_,child:=rangenode.children{ifchild.part==part||child.isWild{//添加参数ifchild.part[0]=='*'{params[child.part[1:]]=strings.Join(searchParts[i:],"/")}ifchild.part[0]==':'{params[child.part[1:]]=part}temp=child.part}}//遇到通配符* , 直接返回iftemp[0]=='*'{returnnode.children[temp],params}node=node.children[temp]}return}上面的代码是我自己实现的一个web框架**gaga**[3]中路由前缀树相关的代码 , 有需要的可以去看看源代码 。 另外 , 欢迎star呀 。
推荐阅读
- 科学家|本可以改变世界,但却未能真正实现的10项发明和研究
- 北京日报客户端|中奥学者研究量子通信获重要进展!首次实现高保真度32维量子纠缠态
- 科技实验室|国外黑客通过程序实现体感操作,用乐高马力欧来玩马力欧游戏
- 最美的时光|明年实现量产,领先世界两代工艺,国产芯片迎来重大突破
- 华为|华为突然官宣,“云手机”每台约99/月,实现换道“超车”
- 机圈大坤坤|价格却是后者的5%?,为何小米手环实现苹果表8成的功能
- 科技小学弟资讯号|传统手机怎么办?,手机进入“云”时代!华为实现弯道超车
- 萧山发布|实现5G信号全覆盖!到2021年,萧山5G将发展成什么样?,投入1000亿元
- 树袋熊|轮值董事长郭平正式确认,麒麟芯片终将王者归来,华为断供倒计时
- YouTube|YouTube项目是怎么赚钱的?实现月入2万美刀难不难?