<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                通過上個章節我們已經學會了中間件的用法,在 gin 框架中路由的回調函數、中間件回調函數本質都是一樣的,我相信大家使用已經沒有任何問題了,那么在本章節我們就由淺入深,繼續深度學習中間件. ## 基礎用法 ### 1.中間件的加載(注冊) 注意:Use 函數僅僅只是負責加載(注冊)中間件,不負責調用. ``` // 路由可以載入很多個中間件,很多個到底是多少個? // 想知道答案,那我們繼續追蹤gin源碼去揭曉答案,這里請帶著你的疑問向后學習 backend.Use(authorization.CheckTokenAuth(), 下一個中間件,繼續下一個中間件, 省略很多個...) ``` ## 進階學習 后續知識點對于初學者來說存在一定的難度,主要是因為在 gin 中,加載的中間件函數: `func ( *gin.Context){ }` 在后續執行時會和注冊的路由回調函數在同一個邏輯處執行,中間件函數、路由回調函數都是平行關系,不同的區別是先后順序不同,gin 對這塊邏輯處理是相同的方式,這就需要我們從路由開始追蹤,因此涉及到的代碼會比較多,過程比較復雜. 學完本章節,gin 最核心的主線邏輯也就徹底搞明白了。 這個過程其實就是對一個 request -> response 的全過程源代碼剖析,難度比較大, ### 2.gin 中間件加載過程 ``` // Use 的作用就是首先載入中間件回調函數:func(*Context) // 首先存儲起來,然后等著被后續邏輯調用 // 我們使用 goland ,ctrl+鼠標左鍵,點擊 Use 繼續追蹤gin源碼 func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes { group.Handlers = append(group.Handlers, middleware...) return group.returnObj() } // group.Handlers 定義的最終原型如下,本質上就是 func(*Context) 的切片,存儲很多個回調函數 type HandlersChain []HandlerFunc // type HandlerFunc func(*Context) // 學習到這里,你必須知道的就是: // gin的中間件回調函數全部被存儲在 group.Handlers 變量了 // 這里您對該變量有印象就行,后面還需要繼續使用該變量 ``` 習到這里,我們已經知道中間件處理函數被 `Use` 函數注冊了在了 `group.Handlers` 變量存儲起來了, `Use` 函數可以加載很多個中間件,究竟是多少個,這里我們依然不知道他的具體數量,還有它們什么時候執行,我們也不知道 ... ... 我們只看見 Use 函數最后返回了 `group.returnObj()` ,它是所有路由的處理接口:IRoutes ,已經結束了,我們無法向下追蹤了,那就只能從其他地方入手追蹤了,既然中間件是加載在具體的路由前面,那么它肯定在某個具體的路由被訪問時執行。 ### 3.gin 中間件執行邏輯 這個過程很漫長,邏輯比較復雜,我們分步驟分析,去追蹤 . #### 3.1 一個具體的路由地址被請求后的 gin 源碼究竟是什么 **3.1.1 定義一個具體的路由以及回調函數** ``` // 1.省略其它無關代碼 users.GET("list", func (c *gin.Context){ // 編寫該路由路徑對應的業務處理回調函數 // 我們省略具體過程 ... ... }) ``` **3.1.2** `**users.GET**` **背后的 gin 源碼追蹤分析** ``` // 2.在 goland 中,ctrl+鼠標左鍵 點擊 GET 函數,源代碼如下: func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes { // 很明顯該函數是 GET + relativePath(開發者定義的路由路徑)對應的業務處理邏輯 // 與中間件一樣,他的請求回調函數也可以有很多個,具體數量不知道... // 那么就需要繼續追蹤 group.handle 源代碼 return group.handle(http.MethodGet, relativePath, handlers) } // 3.group.handle 函數源碼 func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { // 將定義的路由相對路徑拼接為完整的絕對路徑 // 因為一個完整的路徑往往和前面定義的路由組路徑有關系,因此需要一系列拼接 absolutePath := group.calculateAbsolutePath(relativePath) // 針對完整路由路徑將關聯的回調函數全部組合出來 // 究竟如何組合,后續繼續追蹤源碼 handlers = group.combineHandlers(handlers) // 將請求方式(GET、POST等)結合完整路徑作為key,處理函數作為 value // 以 key => value 的形式注冊,value 可以是很多個回調函數 group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() } //4. group.combineHandlers 源代碼追蹤 func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { // 看到這里還記得 group.Handlers 變量嗎?如果不記得請查看本章節 2 標題部分 // finalSize 表示 中間件回調函數的數量 + 具體路由的回調函數數量的總和 finalSize := len(group.Handlers) + len(handlers) // 如果 finalSize >= abortIndex 就會發生panic, // abortIndex 的定義值: math.MaxInt8 / 2 , // 在go語言中,官方定義: MaxInt8 = 1<<7 - 1, 表示 1*(2^7)-1,最終值:127 // 那么 abortIndex = 127/2 取整 = 63 // 至此我們終于知道, gin 的中間件函數數量 + 路由回調函數的數量總和最大允許 63 個. if finalSize >= int(abortIndex) { panic("too many handlers") } // 以上條件檢查全部通過后,將中間件回調函數和路由回調函數全部合并在一起存儲 // HandlersChain 本質就是 [] func(*Context) mergedHandlers := make(HandlersChain, finalSize) // group.Handlers 是中間件函數,他在 mergedHandlers 中存儲的順序靠前,也就是索引比較小 copy(mergedHandlers, group.Handlers) // handlers 是路由回調函數,他的存儲位置比中間函數靠后 copy(mergedHandlers[len(group.Handlers):], handlers) // 最終返回中間件函數+路由函數組合在一起的全部回調函數 return mergedHandlers } //5. group.engine.addRoute 源碼分析 // gin的路由是一個很復雜的路由前綴樹算法模型,完整過程很復雜 // 這里我們主要追蹤路由以及回調函數的 存儲/注冊 過程 func (engine *Engine) addRoute(method, path string, handlers HandlersChain) { assert1(path[0] == '/', "path must begin with '/'") assert1(method != "", "HTTP method can not be empty") assert1(len(handlers) > 0, "there must be at least one handler") debugPrintRoute(method, path, handlers) root := engine.trees.get(method) if root == nil { root = new(node) root.fullPath = "/" engine.trees = append(engine.trees, methodTree{method: method, root: root}) } // 在這里,gin 將我們定義的完整路徑和回調函數進行了注冊 // 源碼后續繼續追、分析 root.addRoute(path, handlers) // Update maxParams if paramsCount := countParams(path); paramsCount > engine.maxParams { engine.maxParams = paramsCount } } //6.root.addRoute(path, handlers) 函數在按照鍵 => 值進行注冊路由與回調函數時調用了很多其他函數 // 這里我將過程函數名字列舉如下 func (n *node) addRoute(path string, handlers HandlersChain) { // 省略其他無關代碼 // 又繼續調用如下函數 n.insertChild(path, fullPath, handlers) // ... ... } // 省略很多其他的代碼 } // 7. n.insertChild func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) { //省略其他無關代碼 child := &node{ priority: 1, fullPath: fullPath, // 完整的路由路徑 } // 最終所有的回調函數 n.handlers = handlers } // node 的結構體定義,發現就是自己嵌套自己的一個結構體 // handlers 成員的數據類型(HandlersChain)本質就是 []func(*gin.Context) // 至此我們徹底明白:路由的存儲模型是一個樹形結構,每個節點都有自己路由路徑以及回調函數 handlers type node struct { path string indices string wildChild bool nType nodeType priority uint32 children []*node // child nodes, at most 1 :param style node at the end of the array handlers HandlersChain fullPath string } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看