如何实现对Go 标准库 HttpServeMux 40倍性能提升

HttpRouter 是一种轻量级高性能的HTTP请求路由器(也称为多路转换器)。与Go官方的默认router 相比 net/http HttpServeMux,此路由器支持动态路由,即路由地址中的一部分可以变成handler参数,并与request方法匹配。此外,它的扩展性也更好。

github 地址: github.com/julienschmi…

性能 benchmark 比较

内存消耗 Static Routes
HttpServeMux 18064 B 706222 ns/op
HttpRouter 26232 B 15010 ns/op

从该表中可以看到,HttpRouter 在静态路由中比go标准库中的实现效率高出了40多倍。尽管内存占用会更高一点,但在现代计算机中,少量的内存占用早已不是瓶颈。

字典树 Trie

又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。
image.png

HttpRouter 如何 work

HttpServeMux的缺点

Go 官方的 HttpServeMux 是通过 hashMap 来保存路由。这个数据结构决定了他无法很好的支持动态路由以及高效的查找。

HttpRouter 的路由实现

HttpRouter 针对高性能和较小的内存占用进行了优化。即使在非常长的路径和大量路线的情况下,它也可以很好地扩展。HttpRouter 依靠我们熟悉的字典树 Trie 来存储 路由地址。它基本上是一个紧凑的前缀树(Radix树)。具有公共前缀的节点也共享一个公共父节点。下面是一个简短的示例,GET request 方法的路由树如下所示:

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>
复制代码

Handle下,每个*<num>代表处理程序函数(指针)的内存地址, 。

如果沿着从树根Root到叶子节点的路径,则将获得完整的路由路径,例如 \blog:post\,其中:post 仅是占位符(参数)。与hashMap不同,树形结构还允许我们使用诸如:post参数的动态部分,因为我们实际上是根据路由模式进行匹配的,而不仅仅是比较哈希。如基准测试所示,此方法非常有效。

由于URL路径具有层次结构,并且仅使用有限的字符集(字节值),因此很可能存在许多公共前缀。这使我们能够轻松地将路由减少到越来越小的问题中。此外,路由器为每种请求方法 POST/GET/DELETE等 管理一棵单独的树。一方面,它比在每个节点中都保存method-> handle,对比map更加节省空间,它还使我们甚至可以在开始在前缀树中查找之前大大减少路由问题。

总结

HttpRouter 使用了字典树来保存路由信息,并且为每种请求方法都单独创建一颗字典树。此外,他还通过 sync.Pool 回收管理 Param 对象,降低GC成本。

欢迎关注我微信公众号,极客行路。 求大哥们点赞~~

image.png

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享