成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開發 前端
對于 Web 框架來說,路由管理是必不可少的一部分,那么本文就來探討一下 go-zero 的路由管理是怎么做的,具體采用了哪種技術方案。

go-zero 是一個微服務框架,包含了 web 和 rpc 兩大部分。

而對于 web 框架來說,路由管理是必不可少的一部分,那么本文就來探討一下 go-zero 的路由管理是怎么做的,具體采用了哪種技術方案。

路由管理方案

路由管理方案有很多種,具體應該如何選擇,應該根據使用場景,以及實現的難易程度做綜合分析,下面介紹常見的三種方案。

注意這里只是做一個簡單的概括性對比。

標準庫方案

最簡單的方案就是直接使用 map[string]func() 作為路由的數據結構,鍵為具體的路由,值為具體的處理方法。

// 路由管理數據結構

type ServeMux struct {
    mu    sync.RWMutex          // 對象操作讀寫鎖
    m     map[string]muxEntry   // 存儲路由映射關系
}

這種方案優點就是實現簡單,性能較高;缺點也很明顯,占用內存更高,更重要的是不夠靈活。

Trie Tree

Trie Tree 也稱為字典樹或前綴樹,是一種用于高效存儲和檢索、用于從某個集合中查到某個特定 key 的數據結構。

圖片

Trie Tree 時間復雜度低,和一般的樹形數據結構相比,Trie Tree 擁有更快的前綴搜索和查詢性能。

和查詢時間復雜度為 O(1) 常數的哈希算法相比,Trie Tree 支持前綴搜索,并且可以節省哈希函數的計算開銷和避免哈希值碰撞的情況。

最后,Trie Tree 還支持對關鍵字進行字典排序。

Radix Tree

Radix Tree(基數樹)是一種特殊的數據結構,用于高效地存儲和搜索字符串鍵值對,它是一種基于前綴的樹狀結構,通過將相同前綴的鍵值對合并在一起來減少存儲空間的使用。

圖片

Radix Tree 通過合并公共前綴來降低存儲空間的開銷,避免了 Trie Tree 字符串過長和字符集過大時導致的存儲空間過多問題,同時公共前綴優化了路徑層數,提升了插入、查詢、刪除等操作效率。

比如 Gin 框架使用的開源組件 HttpRouter 就是采用這個方案。

go-zero 路由規則

在使用 go-zero 開發項目時,定義路由需要遵守如下規則:

  1. 路由必須以 / 開頭
  2. 路由節點必須以 / 分隔
  3. 路由節點中可以包含 :,但是 : 必須是路由節點的第一個字符,: 后面的節點值必須要在結請求體中有 path tag 聲明,用于接收路由參數
  4. 路由節點可以包含字母、數字、下劃線、中劃線

接下來就讓我們深入到源碼層面,相信看過源碼之后,你就會更懂這些規則的意義了。

go-zero 源碼實現

首先需要說明的是,底層數據結構使用的是二叉搜索樹,還不是很了解的同學可以看這篇文章:使用 Go 語言實現二叉搜索樹。

節點定義

先看一下節點定義:

// core/search/tree.go

const (
    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 中的變量,到時候需要替換成實際值。

舉一個例子,有這樣一個路由 /api/:user,那么 api 會存在 children[0],user 會存在 children[1]。

具體可以看看這段代碼:

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
}

主要部分代碼都已經加了注釋,其實這個過程就是樹的構建,如果讀過之前那篇文章,那這里還是比較好理解的。

路由查找

先來看一段 match 代碼:

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,
    }
}

這里有兩個參數:

  • pat:路由樹中存儲的路由。
  • token:實際請求的路由,可能包含參數值。

還是剛才的例子 /api/:user,如果是 api,沒有以 : 開頭,那就不會走 if 邏輯。

接下來匹配 :user 部分,如果實際請求的 url 是 /api/zhangsan,那么會將 user 作為 key,zhangsan 作為 value 保存到結果中。

下面是搜索查找代碼:

// 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 多行,邏輯也并不復雜,通讀之后還是很有收獲的。

大家如果感興趣的話,可以找到項目更詳細地閱讀。也可以關注我,接下來還會分析其他模塊的源碼。

責任編輯:姜華 來源: AlwaysBeta
相關推薦

2023-08-10 08:00:42

令牌限流器計數器

2023-08-28 08:00:45

2025-05-26 04:00:00

2024-04-28 14:46:55

gozero微服務技巧

2012-03-12 16:42:54

測試

2024-01-15 07:42:37

Figma協同編輯算法

2024-04-22 08:26:37

協同編輯FigmaOT 算法

2011-08-01 09:08:49

程序員

2019-11-06 09:39:42

云成本企業云計算

2015-08-20 11:09:53

準入控制盈高

2022-12-07 11:21:30

Reactdiff

2021-07-06 10:03:05

軟件開發 技術

2017-11-16 21:21:18

DevOps測試軟件開發

2021-05-13 08:00:00

軟件測試程序IT

2017-11-23 19:14:00

CRM數字化軟件開發

2019-09-15 14:07:49

2015-07-30 11:21:16

代碼審查

2023-01-18 23:52:07

RTA用戶粒度運營

2022-08-29 08:08:58

SQLOracleCPU

2022-08-03 09:11:31

React性能優化
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产成人免费观看 | 99精品在线观看 | 天天干成人网 | 久久精品91久久久久久再现 | 国产精品久久二区 | 国产免费一区二区三区最新6 | 久久精品欧美电影 | 欧美日韩一 | 黑人精品欧美一区二区蜜桃 | caoporon| 国产资源视频 | 久久精品网 | 91精品国产综合久久精品图片 | 亚洲精品视频在线看 | 精彩视频一区二区三区 | 久久久欧洲 | 欧美一区二区三区视频在线观看 | 亚洲一区二区三区久久 | 久久精品国产v日韩v亚洲 | 亚洲视频区| 成人综合伊人 | 久草免费在线视频 | 午夜影院黄 | 特级a欧美做爰片毛片 | 老司机成人在线 | 在线观看av网站永久 | 国产免费一区二区三区网站免费 | 亚洲免费人成在线视频观看 | 欧美日韩亚洲国产综合 | 91福利电影在线观看 | 三级成人在线观看 | 97人人澡人人爽91综合色 | 不卡视频一区 | 乱一性一乱一交一视频a∨ 色爱av | 欧美精品91 | 成人免费视频在线观看 | 国产成人精品一区二区三区 | 欧美一级淫片007 | 污污免费网站 | 欧美视频一区二区三区 | 久免费视频 |