eudore router radix

RouterCoreStd是使用Radix算法独立实现的高性能路由器,

Radix基础

RouterCoreStd是基于基数树(Radix)实现,压缩前缀树,是一种更节省空间的Trie(前缀树)。对于基数树的每个节点,如果该节点是唯一的子树的话,就和父节点合并。

如果依次向树添加test、team、api,那么过程应该如下,test和team具有公共前缀te,te是st和am公共前缀。

添加test,只有唯一子节点。

test

添加team,team和test具有相同前缀te,那么提取te为公共前缀,然后子节点有两个,分叉成st和am。

te
--st
--am

添加api,api和te没有相同前缀(首字母不同),给根节点添加一个新的子节点api。

te
--st
--am
api

如果需要查找应该字符串,匹配字符串的和节点字符串是否为查找的字符串前缀,是才表示匹配,然后截取剩余字符串,进行下一步匹配。

如果查找append,根节点只有te、app、interface三个子节点,匹配命中app,剩余未匹配的是le。

然后使用a的子节点le、end,两个子节点匹配恰好le。

插入和查找的radix实现:

RouterCoreRadix

RouterCoreRadix实现

RouterCoreRadix相关代码以及移除,当前默认路由核心为RouterCoreStd,由RouterCoreFull重命名,RouterCoreFull为RouterCoreRadix的强化版本。

RouterCoreRadix基于radix tree算法实现,使用节点按类型分类处理,实现匹配优先顺序、易扩展、低代码复杂度的特点是eudore当前使用的默认路由器

RouterCoreRadix支持4种路径,'\'结尾的常量、常量、':name'形式的变量、'*'结尾的通配符;第一个路径空格后可以增加额外的匹配命中参数;而RouterCoreFull额外增加了两种校验类型。

一二是常量,三是通配符和附加参数、四是参数、五是通配符。

RouterCoreRadix代码复杂度均低于15,路由器实现中只存在两处代码复杂度大于15(17,18),由于RouterCoreRadix处理节点类型增加两种导致的,代码复杂度

Radix树只是基本的字符串匹配,但是Radix路由具有常量、变量、通配符三种匹配节点,因此将三种分开处理。

radixNode的kind、path、name保存着结点类型、当前匹配路径、匹配的变量名称;CPW(const、param、wildcard)就是三类结点的集合,tags和vals是默认参数、handlers是多个请求处理者。

HandleFunc

HandleFunc注册Any方法会给全部方法树都注册一遍。

调用insertRoute方法来实现添加,同时将中间件树匹配到的请求中间件和处理者合并,从全部处理中间件和多个请求处理者合并成一个完整请求处理链。

insertRoute方法先拿到对应的基数树,如果方法是不允许的就拿到的405树,就结束了添加。

然后将路由注册路径按结点类型切割成多段,按照常量、变量、字符串三类将路径切割出来,每类结点就是一种结点的类型,当前切割实现对应特殊的正则规则支持不够。

在查找时先匹配全部常量子节点,没有就使用变量子节点,uri本段就是变量内容,剩余进行递归匹配,如果变量子节点不匹配,就检查通配符节点,如果存在就是直接匹配通配符。

因此具有路由具有严格的匹配优先顺序,一定是先常量再变量最后通配符,由匹配函数里面代码段的位置决定了顺序。

如果六条路由是/*,最先注册的,但是api是常量更有效,就会先检查是否是api,不是才会使用通配符,

/api/:user/api/:user/info两条,会进一步检查是否是info,如果是/api/eudore/list只会匹配到/api/*

func getSpiltPath(key string) []string将字符串按Node类型切割。

例如/api/:get/*:get*明显是变量和通配符节点。所以两种前后需要切分开来,结果为[/api/ :get / *],/api/增加变量子节点:get,依次添加完成树。

字符串路径切割例子:

然后给基数树添加结点,如果结点是存在的就返回的存在的,所以要更新当前的根节点,然后依次向下添加。

最后一个结点就是树底了,然后给树底的新结点设置多个请求处理者和默认参数。

如果添加方法是Any,那么kind会增加一个Any的flag,允许非Any方法覆盖Any方法反之不行。

newRadixNode函数就是简单的根据首字符来设置结点的类型和名称。

InsertNode方法就根据结点类型来添加对对应的结点集合中,常量结点需要进行分叉操作,将相同的前缀提取出来。

Match

Match是匹配方法,先使用getTree方法获得请求方法对应的树进行查找,如果405就方法的405树,只会匹配405的结果;如果其他方法树就进行路由匹配,匹配结果非空则返回;如果查找的结果是空,就使用404处理。

recursiveLoopup方法递归查找主要分为四步。

第一步检测当前节点是否匹配,匹配则添加参数然后返回。

第二步检测当前节点的常量子节点是否和匹配路径前缀,有前缀表示有可能匹配到了,然后截取路径递归匹配,然后不为空就表示匹配命中,返回对象

第三步检测当前节点的变量子节点是否匹配,直接截取两个‘/’间路径为当前的变量匹配内容,然后检测进一步匹配。

第四步检测当前节点是否拥有通配符结点,如果有直接执行通配符匹配。

最后如果前面四步没有匹配名字,返回nil。

Last updated

Was this helpful?