当前位置:首页 > 科技  > 软件

Go-Zero 是如何做路由管理的?

来源: 责编: 时间:2023-08-09 23:03:50 193观看
导读go-zero 是一个微服务框架,包含了 web 和 rpc 两大部分。而对于 web 框架来说,路由管理是必不可少的一部分,那么本文就来探讨一下 go-zero 的路由管理是怎么做的,具体采用了哪种技术方案。路由管理方案路由管理方案有很多

HAG28资讯网——每日最新资讯28at.com

go-zero 是一个微服务框架,包含了 web 和 rpc 两大部分。HAG28资讯网——每日最新资讯28at.com

而对于 web 框架来说,路由管理是必不可少的一部分,那么本文就来探讨一下 go-zero 的路由管理是怎么做的,具体采用了哪种技术方案。HAG28资讯网——每日最新资讯28at.com

路由管理方案

路由管理方案有很多种,具体应该如何选择,应该根据使用场景,以及实现的难易程度做综合分析,下面介绍常见的三种方案。HAG28资讯网——每日最新资讯28at.com

注意这里只是做一个简单的概括性对比。HAG28资讯网——每日最新资讯28at.com

标准库方案

最简单的方案就是直接使用 map[string]func() 作为路由的数据结构,键为具体的路由,值为具体的处理方法。HAG28资讯网——每日最新资讯28at.com

// 路由管理数据结构type ServeMux struct {    mu    sync.RWMutex          // 对象操作读写锁    m     map[string]muxEntry   // 存储路由映射关系}

这种方案优点就是实现简单,性能较高;缺点也很明显,占用内存更高,更重要的是不够灵活。HAG28资讯网——每日最新资讯28at.com

Trie Tree

Trie Tree 也称为字典树或前缀树,是一种用于高效存储和检索、用于从某个集合中查到某个特定 key 的数据结构。HAG28资讯网——每日最新资讯28at.com

图片HAG28资讯网——每日最新资讯28at.com

Trie Tree 时间复杂度低,和一般的树形数据结构相比,Trie Tree 拥有更快的前缀搜索和查询性能。HAG28资讯网——每日最新资讯28at.com

和查询时间复杂度为 O(1) 常数的哈希算法相比,Trie Tree 支持前缀搜索,并且可以节省哈希函数的计算开销和避免哈希值碰撞的情况。HAG28资讯网——每日最新资讯28at.com

最后,Trie Tree 还支持对关键字进行字典排序。HAG28资讯网——每日最新资讯28at.com

Radix Tree

Radix Tree(基数树)是一种特殊的数据结构,用于高效地存储和搜索字符串键值对,它是一种基于前缀的树状结构,通过将相同前缀的键值对合并在一起来减少存储空间的使用。HAG28资讯网——每日最新资讯28at.com

图片HAG28资讯网——每日最新资讯28at.com

Radix Tree 通过合并公共前缀来降低存储空间的开销,避免了 Trie Tree 字符串过长和字符集过大时导致的存储空间过多问题,同时公共前缀优化了路径层数,提升了插入、查询、删除等操作效率。HAG28资讯网——每日最新资讯28at.com

比如 Gin 框架使用的开源组件 HttpRouter 就是采用这个方案。HAG28资讯网——每日最新资讯28at.com

go-zero 路由规则

在使用 go-zero 开发项目时,定义路由需要遵守如下规则:HAG28资讯网——每日最新资讯28at.com

  1. 路由必须以 / 开头
  2. 路由节点必须以 / 分隔
  3. 路由节点中可以包含 :,但是 : 必须是路由节点的第一个字符,: 后面的节点值必须要在结请求体中有 path tag 声明,用于接收路由参数
  4. 路由节点可以包含字母、数字、下划线、中划线

接下来就让我们深入到源码层面,相信看过源码之后,你就会更懂这些规则的意义了。HAG28资讯网——每日最新资讯28at.com

go-zero 源码实现

首先需要说明的是,底层数据结构使用的是二叉搜索树,还不是很了解的同学可以看这篇文章:使用 Go 语言实现二叉搜索树。HAG28资讯网——每日最新资讯28at.com

节点定义

先看一下节点定义:HAG28资讯网——每日最新资讯28at.com

// core/search/tree.goconst (    colon = ':'    slash = '/')type (    // 节点    node struct {        item     interface{}        children [2]map[string]*node    }    // A Tree is a search tree.    Tree struct {        root *node    })

重点说一下 children,它是一个包含两个元素的数组,元素 0 存正常路由键,元素 1 存以 : 开头的路由键,这些是 url 中的变量,到时候需要替换成实际值。HAG28资讯网——每日最新资讯28at.com

举一个例子,有这样一个路由 /api/:user,那么 api 会存在 children[0],user 会存在 children[1]。HAG28资讯网——每日最新资讯28at.com

具体可以看看这段代码:HAG28资讯网——每日最新资讯28at.com

func (nd *node) getChildren(route string) map[string]*node {    // 判断路由是不是以 : 开头    if len(route) > 0 && route[0] == colon {        return nd.children[1]    }    return nd.children[0]}

路由添加

// Add adds item to associate with route.func (t *Tree) Add(route string, item interface{}) error {    // 需要路由以 / 开头    if len(route) == 0 || route[0] != slash {        return errNotFromRoot    }    if item == nil {        return errEmptyItem    }    // 把去掉 / 的路由作为参数传入    err := add(t.root, route[1:], item)    switch err {    case errDupItem:        return duplicatedItem(route)    case errDupSlash:        return duplicatedSlash(route)    default:        return err    }}func add(nd *node, route string, item interface{}) error {    if len(route) == 0 {        if nd.item != nil {            return errDupItem        }        nd.item = item        return nil    }    // 继续判断,看看是不是有多个 /    if route[0] == slash {        return errDupSlash    }    for i := range route {        // 判断是不是 /,目的就是去处两个 / 之间的内容        if route[i] != slash {            continue        }        token := route[:i]                // 看看有没有子节点,如果有子节点,就在子节点下面继续添加        children := nd.getChildren(token)        if child, ok := children[token]; ok {            if child != nil {                return add(child, route[i+1:], item)            }            return errInvalidState        }        // 没有子节点,那么新建一个        child := newNode(nil)        children[token] = child        return add(child, route[i+1:], item)    }    children := nd.getChildren(route)    if child, ok := children[route]; ok {        if child.item != nil {            return errDupItem        }        child.item = item    } else {        children[route] = newNode(item)    }    return nil}

主要部分代码都已经加了注释,其实这个过程就是树的构建,如果读过之前那篇文章,那这里还是比较好理解的。HAG28资讯网——每日最新资讯28at.com

路由查找

先来看一段 match 代码:HAG28资讯网——每日最新资讯28at.com

func match(pat, token string) innerResult {    if pat[0] == colon {        return innerResult{            key:   pat[1:],            value: token,            named: true,            found: true,        }    }    return innerResult{        found: pat == token,    }}

这里有两个参数:HAG28资讯网——每日最新资讯28at.com

  • pat:路由树中存储的路由。
  • token:实际请求的路由,可能包含参数值。

还是刚才的例子 /api/:user,如果是 api,没有以 : 开头,那就不会走 if 逻辑。HAG28资讯网——每日最新资讯28at.com

接下来匹配 :user 部分,如果实际请求的 url 是 /api/zhangsan,那么会将 user 作为 key,zhangsan 作为 value 保存到结果中。HAG28资讯网——每日最新资讯28at.com

下面是搜索查找代码:HAG28资讯网——每日最新资讯28at.com

// Search searches item that associates with given route.func (t *Tree) Search(route string) (Result, bool) {    // 第一步先判断是不是 / 开头    if len(route) == 0 || route[0] != slash {        return NotFound, false    }    var result Result    ok := t.next(t.root, route[1:], &result)    return result, ok}func (t *Tree) next(n *node, route string, result *Result) bool {    if len(route) == 0 && n.item != nil {        result.Item = n.item        return true    }    for i := range route {        // 和 add 里同样的提取逻辑        if route[i] != slash {            continue        }        token := route[:i]        return n.forEach(func(k string, v *node) bool {            r := match(k, token)            if !r.found || !t.next(v, route[i+1:], result) {                return false            }            // 如果 url 中有参数,会把键值对保存到结果中            if r.named {                addParam(result, r.key, r.value)            }            return true        })    }    return n.forEach(func(k string, v *node) bool {        if r := match(k, route); r.found && v.item != nil {            result.Item = v.item            if r.named {                addParam(result, r.key, r.value)            }            return true        }        return false    })}

以上就是路由管理的大部分代码,整个文件也就 200 多行,逻辑也并不复杂,通读之后还是很有收获的。HAG28资讯网——每日最新资讯28at.com

大家如果感兴趣的话,可以找到项目更详细地阅读。也可以关注我,接下来还会分析其他模块的源码。HAG28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-5196-0.htmlGo-Zero 是如何做路由管理的?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 我们一起聊聊抽象工厂模式(AbstractFactoty)

下一篇: 三言两语说透webpack对vue的编译

标签:
  • 热门焦点
  • 对标苹果的灵动岛 华为带来实况窗功能

    对标苹果的灵动岛 华为带来实况窗功能

    继苹果的灵动岛之后,华为也在今天正式推出了“实况窗”功能。据今天鸿蒙OS 4.0的现场演示显示,华为的实况窗可以更高效的展现出实时通知,比如锁屏上就能看到外卖、打车、银行
  • JavaScript学习 -AES加密算法

    JavaScript学习 -AES加密算法

    引言在当今数字化时代,前端应用程序扮演着重要角色,用户的敏感数据经常在前端进行加密和解密操作。然而,这样的操作在网络传输和存储中可能会受到恶意攻击的威胁。为了确保数据
  • 小红书1周涨粉49W+,我总结了小白可以用的N条涨粉笔记

    小红书1周涨粉49W+,我总结了小白可以用的N条涨粉笔记

    作者:黄河懂运营一条性教育视频,被54万人“珍藏”是什么体验?最近,情感博主@公主是用鲜花做的,火了!仅仅凭借一条视频,光小红书就有超过128万人,为她疯狂点赞!更疯狂的是,这
  • 雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    雅柏威士忌多款单品价格大跌,泥煤顶流也不香了?

    来源 | 烈酒商业观察编 | 肖海林今年以来,威士忌市场开始出现了降温迹象,越来越多不断暴涨的网红威士忌也开始悄然回归市场理性。近日,LVMH集团旗下苏格兰威士忌品牌雅柏(Ardbeg
  • 网红炒股不为了赚钱,那就是耍流氓!

    网红炒股不为了赚钱,那就是耍流氓!

    来源:首席商业评论6月26日高调宣布入市,网络名嘴大v胡锡进居然进军了股市。在一次财经媒体峰会上,几个财经圈媒体大佬就“胡锡进炒股是否知道认真报道”展开讨论。有
  • 消息称小米汽车开始筛选交付中心:需至少120个车位

    消息称小米汽车开始筛选交付中心:需至少120个车位

    IT之家 7 月 7 日消息,日前,有微博简介为“汽车行业从业者、长三角一体化拥护者”的微博用户 @长三角行健者 发文表示,据经销商集团反馈,小米汽车目前
  •  首发天玑9200+ iQOO Neo8系列发布首销售价2299元起

    首发天玑9200+ iQOO Neo8系列发布首销售价2299元起

    2023年5月23日晚,iQOO Neo8系列正式发布。其中,Neo系列首款Pro之作——iQOO Neo8 Pro强悍登场,限时售价3099元起;价位段最强性能手机iQOO Neo8同期上市
  • 滴滴违法违规被罚80.26亿 共存在16项违法事实

    滴滴违法违规被罚80.26亿 共存在16项违法事实

    滴滴违法违规被罚80.26亿 存在16项违法事实开始于2121年7月,历经一年时间,网络安全审查办公室对“滴滴出行”网络安全审查终于有了一个暂时的结束。据“网信
  • SN570 NVMe SSD固态硬盘 价格与性能兼具

    SN570 NVMe SSD固态硬盘 价格与性能兼具

    SN570 NVMe SSD固态硬盘是西部数据发布的最新一代WD Blue系列的固态硬盘,不仅闪存技术更为精进,性能也得到了进一步的跃升。WD Blue SN570 NVMe SSD的包装外
Top