gin源碼分析

一、高性能
  • 使用sync.pool解決頻繁創建的context對象,在百萬并發的場景下能大大提供訪問性能和減少GC

    // ServeHTTP conforms to the http.Handler interface.
    // 每次的http請求都會從sync.pool中獲取context,用完之后歸還到pool中
    func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {c := engine.pool.Get().(*Context)c.writermem.reset(w) //重置 responsewriterc.Request = reqc.reset() //重置使用過的context各個屬性engine.handleHTTPRequest(c)engine.pool.Put(c)
    }// 這里給pool指定了一個創建新對象的函數,注意不是所有的請求共用一個context,context在高并發場景下可能會分配多個,但是遠遠小于并發協程數量。 sync.pool.new是可能并發調用的,所以內部的邏輯需要保障線程安全
    func New(opts ...OptionFunc) *Engine {... engine.RouterGroup.engine = engineengine.pool.New = func() any {return engine.allocateContext(engine.maxParams)}return engine.With(opts...)
    }//每次http請求都需要分配一個context,這個初始context初始化了兩個數組的最大容量
    func (engine *Engine) allocateContext(maxParams uint16) *Context {v := make(Params, 0, maxParams)skippedNodes := make([]skippedNode, 0, engine.maxSections)return &Context{engine: engine, params: &v, skippedNodes: &skippedNodes}
    }
    
  • 前綴樹路由(類似httprouter的路由,提升性能近40倍)

    gin在v1.0版本開始放棄了 github.com/julienschmidt/httprouter,重新實現了一套路由;對比gin新實現的路由基本上采用了httprouter的邏輯,但是和框架結合的更加完整,比如說把httprouter中router的能力提到了engine中。

  • json序列化優化

    gin提供了四種可選的json序列化方式,默認情況下會使用encoding/json

    /github.com/gin-gonic/gin@v1.10.0/internal/json-- go_json.go ("github.com/goccy/go-json")-- json.go  ("encoding/json")-- jsoniter.go ("github.com/json-iterator/go")-- sonic.go ("github.com/bytedance/sonic")
    

    需要在編譯期間指定tag來決定使用哪種序列化工具

    go run -tags={go_json|jsoniter|sonic|(不指定默認encoding)} main.go
    

    通過一個簡單的基準測試看看哪種json序列化效率更高

    package ganimport ("encoding/json"sonicjson "github.com/bytedance/sonic"gojson "github.com/goccy/go-json"itjson "github.com/json-iterator/go""testing"
    )func BenchmarkJson(b *testing.B) {jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`m := &map[string]interface{}{}for i := 0; i < b.N; i++ {json.Unmarshal([]byte(jsonStr), m)json.Marshal(m)}
    }func BenchmarkGOJson(b *testing.B) {jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`m := &map[string]interface{}{}for i := 0; i < b.N; i++ {gojson.Unmarshal([]byte(jsonStr), m)gojson.Marshal(m)}
    }func BenchmarkItJson(b *testing.B) {m := &map[string]interface{}{}jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`for i := 0; i < b.N; i++ {itjson.Unmarshal([]byte(jsonStr), m)itjson.Marshal(m)}
    }func BenchmarkSonicJson(b *testing.B) {m := &map[string]interface{}{}jsonStr := `{"name":"zhangsan","age":18,"address":"beijing"}`for i := 0; i < b.N; i++ {sonicjson.Unmarshal([]byte(jsonStr), m)sonicjson.Marshal(m)}
    }
    

    測試結果: sonic > go_json > json_iterator > encoding

    $ go test -bench='Json$' -benchtime=5s -benchmem .
    goos: windows
    goarch: amd64
    pkg: gan
    cpu: Intel(R) Core(TM) i5-10400 CPU @ 2.90GHz
    BenchmarkJson-12                 2632854              2260 ns/op             616 B/op         24 allocs/op
    BenchmarkGOJson-12               5226374              1142 ns/op             248 B/op         11 allocs/op
    BenchmarkItJson-12               4811112              1260 ns/op             400 B/op         19 allocs/op
    BenchmarkSonicJson-12            6616218               913.0 ns/op           333 B/op         10 allocs/op
    PASS
    ok      gan     30.313s
    
二、基于前綴樹的路由設計
  • go語言中原生net/http包在負載路由下的缺陷

    動態路由:缺少例如hello/:name,hello/*這類的規則。
    鑒權:沒有分組/統一鑒權的能力,需要在每個路由映射的handler中實現。

  • http請求怎么進入的gin的處理邏輯中去的?

    1. gin框架中調用了net/http包,監聽address,請求都會進入頂層路由,也就是engine結構實現的ServeHTTP函數中

      func (engine *Engine) Run(addr ...string) (err error) {......debugPrint("Listening and serving HTTP on %s\n", address)err = http.ListenAndServe(address, engine.Handler())return
      }
      func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {......engine.handleHTTPRequest(c)......
      }
      
    2. 在engine.handleHTTPRequest? 中,根據request中的path,在路由表中獲取路由對應的handler然執行

      func (engine *Engine) handleHTTPRequest(c *Context) {httpMethod := c.Request.MethodrPath := c.Request.URL.Path......// Find root of the tree for the given HTTP methodt := engine.treesfor i, tl := 0, len(t); i < tl; i++ {if t[i].method != httpMethod {continue}root := t[i].root// Find route in treevalue := root.getValue(rPath, c.params, c.skippedNodes, unescape)if value.params != nil {c.Params = *value.params}if value.handlers != nil {c.handlers = value.handlersc.fullPath = value.fullPathc.Next() //最終執行的handlerc.writermem.WriteHeaderNow()return}......}
      }
      
  • 路由的注冊和匹配

    1. Engine繼承了RouterGroup的能力,當我們注冊handler時,或者在分組路由下去注冊handler時,其實都是調用的RouterGroup的能力

      type Engine struct {RouterGroup......
      }
      
    2. RouterGroup中保留了一個Engine指針,最終的路由是注冊到了Engine中的路由表中,這個路由表沒有設計在RouterGroup中,保證了RouterGroup的職責單一性,也就是只做路由分組。

      func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {......group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj()
      }
      
    3. Engine中保存了一個methodTree對象,這個就是路由表,是一個樹狀結構

      img

      type methodTree struct {method stringroot   *node
      }type node struct {path      stringindices   stringwildChild boolnType     nodeTypepriority  uint32children  []*node // child nodes, at most 1 :param style node at the end of the arrayhandlers  HandlersChainfullPath  string
      }type methodTrees []methodTree
      
三、中間件執行流程
  • 中間件執行鏈路

    1. 中間件保存在GroupRouter中

      func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {group.Handlers = append(group.Handlers, middleware...)return group.returnObj()
      }
      
    2. 每聲明一次分組,都會將handler進行一次合并

      func (group *RouterGroup) Group(relativePath string, handlers ...HandlerFunc) *RouterGroup {return &RouterGroup{Handlers: group.combineHandlers(handlers), //合并中間處理鏈路basePath: group.calculateAbsolutePath(relativePath),engine:   group.engine,}
      }
      
    3. 以上只是將中間件合并成了一條鏈路,最終的業務邏輯會在執行GET.POST等請求方法時,拼接到執行鏈路末端

      func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath := group.calculateAbsolutePath(relativePath)handlers = group.combineHandlers(handlers) //這里參數的handlers是業務邏輯,合并到鏈路末尾。group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj()
      }
    4. 最終效果img

  • 前置處理和后置處理

    1. 假設需要對一段業務邏輯采集它的執行耗時,一般我們需要在執行邏輯前聲明一個時間點,執行完后再用當前時間減去執行前的時間得到耗時。

      img

    2. 在gin中的實現方式是通過context的next方法去實現的,結合上面的流程,在中間件中每聲明一次next,執行鏈會先去執行下一個handler,最終的效果應該是ABBA方式的執行。

      func (c *Context) Next() {c.index++for c.index < int8(len(c.handlers)) {c.handlers[c.index](c)c.index++}
      }
      
其他:

Gin的核心特性

  • 高性能:Gin的HTTP請求處理速度極快,能夠支持大量的并發連接(gin并不是基準測試中最快的框架,但是相對較快,簡單好用,用戶體量大)
  • 簡單易用:Gin的API設計直觀,使得開發者可以快速上手并構建應用。
  • 中間件支持:Gin允許開發者通過中間件來擴展其功能,這為構建復雜的Web應用提供了可能。
  • 路由和參數綁定:Gin提供了強大的路由功能,支持參數綁定,使得URL處理更加靈活。
  • 數據渲染:Gin支持多種數據渲染方式,包括JSON、XML、HTML等,方便開發者根據需求選擇合適的輸出格式。

參考:https://geektutu.com/post/gee-day4.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/44530.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/44530.shtml
英文地址,請注明出處:http://en.pswp.cn/web/44530.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

在C++中怎樣使用C庫

在C中使用C庫是一個相對直接的過程&#xff0c;因為C是從C發展而來的&#xff0c;并且與C高度兼容。這意味著你可以直接在C代碼中使用C庫函數、頭文件和變量&#xff0c;而無需進行特殊轉換。以下是一些基本的步驟和注意事項&#xff0c;用于在C中使用C庫&#xff1a; 1. 包含…

c語言位操作符相關題目之交換兩個數的值

文章目錄 一、題目二、方法11&#xff0c;思路2&#xff0c;代碼實現 三、方法21&#xff0c;思路2&#xff0c;代碼實現 四、方法31&#xff0c;思路2&#xff0c;代碼實現 總結 提示&#xff1a;以下是本篇文章正文內容&#xff0c;下面案例可供參考 一、題目 實現兩個變量的…

淺談PostCSS

1. 背景 css的預處理器語言&#xff08;比如 sass&#xff0c; less&#xff0c; stylus&#xff09;的擴展性不好&#xff0c;你可以使用它們已有的功能&#xff0c;但如果想做擴展就沒那么容易。 sass是很常用的css預處理器語言&#xff0c;在webpack中要使用它&#xff0c;…

設計模式使用場景實現示例及優缺點(結構型模式——組合模式)

結構型模式 組合模式&#xff08;Composite Pattern&#xff09; 組合模式使得用戶對單個對象和組合對象的使用具有一致性。 有時候又叫做部分-整體模式&#xff0c;它使我們樹型結構的問題中&#xff0c;模糊了簡單元素和復雜元素的概念&#xff0c;客戶程序可以像處理簡單元…

小米起訴“小米”商標侵權,索賠500萬!

近日浙江麗水有家叫小米的公司&#xff0c;因為商標侵權被小米科技起訴索賠500萬&#xff0c;需要變更企業名稱&#xff0c;官網也不能用“小米智能大家居”等&#xff0c;還有其它的賠償&#xff0c;普推知產商標老楊分析&#xff0c;“小米智能大家居”“小米”&#xff0c;后…

【Flask從入門到精通:第九課:數據庫基本操作、數據表操作以及數據操作】

數據庫操作 數據庫驅動&#xff08;drivers&#xff09;模塊&#xff1a;pymysql、MySQLDB 數據庫基本操作 在SQLAlchemy中&#xff0c;添加、修改、刪除操作&#xff0c;均由數據庫會話(sessionSM)管理。 會話用 db.session 表示。在準備把數據寫入數據庫前&#xff0c;要先…

交易平臺Zero Hash現已支持SUI交易

Zero Hash是一家領先的加密貨幣和穩定幣基礎設施平臺&#xff0c;為包括Stripe、Shift4和Franklin Templeton在內的公司提供支持&#xff0c;現在也支持對SUI的訪問。此舉使Zero Hash的客戶及其終端用戶能夠使用SUI。 提供API和SDK以及專注于無縫連接法幣、加密貨幣和穩定幣的…

讀人工智能全傳11人工智能會出什么錯

1. 人工智能會出什么錯 1.1. 一些報道是公正合理的&#xff0c;不過坦白地說&#xff0c;大部分報道都愚蠢得無可救藥 1.2. 一些報道頗有知識性和引導性&#xff0c;而大部分則是杞人憂天式的恐嚇 1.3. 滑稽的報道迎合了大眾對人工智能的“終結者式恐懼” 1.3.1. 我們創造出…

html設計(兩種常見的充電效果)

第一種 完整代碼&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title&…

方便快捷傳文件—搭建rsync文件傳輸服務器

比如我們有一個服務器&#xff0c;想把各個機器的文件都通過腳本傳給這臺機&#xff0c;用sftp或者直接rsync就必須輸密碼&#xff0c;肯定不行&#xff0c;做等效性免密又麻煩&#xff0c;怎么辦呢&#xff0c;這么辦&#xff01; 在服務端 yum -y install rsync #編輯&…

Vue3 關于scss預編譯中:deep 其中的deep如何理解

在SCSS預處理器中&#xff0c;:deep是一個偽類選擇器&#xff0c;用于選擇一個元素的所有后代元素&#xff0c;無論它們在DOM結構中的層級深度如何。換句話說&#xff0c;:deep選擇器是一個類似于CSS中的后代選擇器&#xff0c;但是它可以不考慮嵌套層級的限制&#xff0c;而是…

Android MessageQueue 源碼分析

類注釋 /*** Low-level class holding the list of messages to be dispatched by a* {link Looper}. Messages are not added directly to a MessageQueue,* but rather through {link Handler} objects associated with the Looper.** <p>You can retrieve the Messa…

【FineGrip】全光學感知:一種新的通用遙感圖像解釋任務和細粒度數據集(IEEE TGRS 2024)

摘要 目前的遙感解譯模型通常集中于單一的任務&#xff0c;如檢測、分割或字幕。但是&#xff0c;針對特定任務設計的模型無法實現綜合多層次解譯。該領域也缺乏多任務聯合解譯數據集。本文提出Panoptic Perception&#xff0c;一個新的任務和一個新的細粒度數據集&#xff08…

apache Kylin系列介紹及配置

Apache Kylin是一個開源的分布式分析引擎,用于OLAP(聯機分析處理)數據處理。它專門設計用于處理大規模的數據集,并提供快速的查詢和分析能力。 apache Kylin架構: Apache Kylin是一個開源的分布式分析引擎,旨在提供高性能、低延遲的OLAP(聯機分析處理)能力。下面是Ap…

nvim工具介紹

1. **seoul256.vim**: 一個基于首爾色彩的低對比度 Vim 顏色方案&#xff0c;適用于 256 色終端或 GVim。 2. **vim-easy-align**: Vim 插件&#xff0c;用于對齊文本。 3. **vim-go**: Vim 的 Go 語言插件&#xff0c;支持語法高亮、自動縮進等。 4. **coc.nvim**: 集成了 LSP…

GESP CCF C++ 二級認證真題 2024年6月

第 1 題 小楊父母帶他到某培訓機構給他報名參加CCF組織的GESP認證考試的第1級&#xff0c;那他可以選擇的認證語言有幾種&#xff1f;&#xff08; &#xff09; A. 1 B. 2 C. 3 D. 4 第 2 題 下面流程圖在yr輸入2024時&#xff0c;可以判定yr代表閏年&#xff0c;并輸出 2月…

介紹一款數據準實時復制(CDC)中間件 `Debezium`

簡介 文章開頭先介紹一下什么是CDC。數據準實時復制(CDC)是目前行內實時數據需求大量使用的技術。常用的中間件有Canal、Debezium、Flink CDC等 下面我們做一下對比 各有優缺點吧,本主要介紹一下Debezium中間件。 Debezium是什么 Debezium是一個為變更數據捕獲(CDC)提供…

Windows圖形界面(GUI)-SDK-C/C++ - 列表框(List)

公開視頻 -> 鏈接點擊跳轉公開課程博客首頁 -> 鏈接點擊跳轉博客主頁 目錄 列表框(List) 控件樣式 創建控件 初始控件 消息處理 示例代碼 列表框(List) 控件樣式 列表框&#xff08;ListBox&#xff09;是Windows圖形界面開發中常用的控件&#xff0c;允許用戶從…

力扣第230題“二叉搜索樹中第K小的元素”

在本篇文章中&#xff0c;我們將詳細解讀力扣第230題“二叉搜索樹中第K小的元素”。通過學習本篇文章&#xff0c;讀者將掌握如何使用中序遍歷來找到二叉搜索樹中的第K小的元素&#xff0c;并了解相關的復雜度分析和模擬面試問答。每種方法都將配以詳細的解釋&#xff0c;以便于…

OpenAI終止對中國提供API服務,對國內AI市場產生重大沖擊?

6月25日&#xff0c;OpenAI突然宣布終止向包括中國在內的國家地區提供API服務&#xff0c;本月9日這一政策已經正式生效了&#xff01; 有人說&#xff0c;這個事件給中國AI行業帶來很大沖擊&#xff01;是這樣嗎&#xff1f;在展開討論前&#xff0c;我們先來看看什么是API服務…