net/http與gin框架的關系分析

要想學好 gin 框架,首先要學習 net/http 服務,而二者的關系又是重中之重。
本文所要做的任務就是將二者“連接” 起來,讓讀者掌握其中之精髓。

一、Golang HTTP 標準庫示例

使用 golang 啟動 http 服務非常簡單,就是一個標準的 C/S 架構服務,代碼:

package mainimport ("fmt""net/http"
)func pingHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, net/http! v2\n")
}
func main() {http.HandleFunc("/ping", pingHandler)http.ListenAndServe(":8091", nil)
}

這段代碼主要完成了兩件事:

  1. 通過 http.HandleFunc 方法注冊里 處理函數
  2. 啟動 指定端口的 http 服務。

那背后隱藏了什么呢,我們主要致力于挖掘出核心的東西:

  • 路徑注冊、匹配是如何實現的,依托的核心是什么? 關鍵詞:前綴樹、暴露接口
  • http 服務的請求路徑是怎么樣的? 關鍵詞:one-loop 模型

二、Golang HTTP 標準庫 原理

2.1 服務注冊

首先我們圍繞 http.HandleFunc 源碼展開:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)
}// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMuxtype ServeMux struct { // 對 Handler 的具體實現,內部通過一個 map 維護了從 path 到 handler 的映射關系.mu    sync.RWMutexm     map[string]muxEntryes    []muxEntry // slice of entries sorted from longest to shortest.hosts bool       // whether any patterns contain hostnames
}type muxEntry struct {  // 一個 handler 單元,內部包含了請求路徑 path + 處理函數 handler 兩部分.h Handlerpattern string 
}

可以看到,是通過默認數據 defaultServeMux 實現的,該結構重點包含的方法:ServeHTTP 和 HandleFunc

首先講解下為什么 ServeHTTP 方法很重要,因為 ServeMux 是對 Handler 的具體實現:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {h, _ := mux.Handler(r)h.ServeHTTP(w, r)
}

而 Handler 的定義如下:

type Handler interface {ServeHTTP(ResponseWriter, *Request)
}

Handler 是一個 interface,暴露了方法: ServeHTTP,該方法根據 http 請求 Request 中的請求路徑 path 映射到對應的 handler 處理函數,對請求進行處理和響應.

這種實現接口方法有什么好處呢,這里我們先留一個懸念,之后我們可以在后面的請求流程中看到,暫且不表。

其次我們來看 HandleFunc 方法,內部會將處理函數 handler 轉為實現了 ServeHTTP 方法的 HandlerFunc 類型,將其作為 Handler interface 的實現類注冊到 ServeMux 的路由 map 當中.

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {mux.Handle(pattern, HandlerFunc(handler))
}type HandlerFunc func(ResponseWriter, *Request)// Handle registers the handler for the given pattern.
func (mux *ServeMux) Handle(pattern string, handler Handler) {// 將 path 和 handler 包裝成一個 muxEntry,以 path 為 key 注冊到路由 map ServeMux.m 中
}

2.2 服務啟動

http.ListenAndServe 通過調用 net/http 包公開的方法,實現對服務端的一鍵啟動. 內部定義了一個新的 Server 對象,嵌套執行 Server.ListenAndServe 方法:

func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()
}

Server.ListenAndServe 方法中,根據用戶傳入的端口,申請到一個監聽器 listener,繼而調用 Server.Serve 方法.

func (srv *Server) ListenAndServe() error {addr := srv.Addrln, err := net.Listen("tcp", addr)return srv.Serve(ln)
}

Server.Serve 方法很核心,體現了 http 服務端的運行架構:for + listener.accept 模式:

func (srv *Server) Serve(l net.Listener) error {ctx := context.WithValue(baseCtx, ServerContextKey, srv)for {rw, err := l.Accept()// ...connCtx := ctx// ...c := srv.newConn(rw)// ...go c.serve(connCtx)}}
}

主要實現功能:

  • 將 server 封裝成一組 kv 對,添加到 context 當中
  • 開啟 for 循環,每輪循環調用 Listener.Accept 方法阻塞等待新連接到達
  • 每有一個連接到達,創建一個 goroutine 異步執行 conn.serve 方法負責處理

其中 conn.serve 是響應客戶端連接的核心方法:

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {// ...c.r = &connReader{conn: c}c.bufr = newBufioReader(c.r)c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)for {w, err := c.readRequest(ctx)// 核心serverHandler{c.server}.ServeHTTP(w, w.req)}

可以看下核心的實現:

type serverHandler struct {srv *Server
}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerhandler.ServeHTTP(rw, req)
}

在 serveHandler.ServeHTTP 方法中,會對 Handler 作判斷,倘若其未聲明,則取全局單例 DefaultServeMux 進行路由匹配,呼應了 http.HandleFunc 中的處理細節。

基于接口而非實現,此后開始調用實現的 ServeHTTP 方法,匹配到相應的處理函數后執行:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {h, _ := mux.Handler(r)h.ServeHTTP(w, r)
}func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {return mux.handler(host, r.URL.Path)
}func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()h, pattern = mux.match(path)
}

三、Gin 框架原理

Gin 是在 Golang HTTP 標準庫 net/http 基礎之上的再封裝,兩者的交互邊界圖。

可以看出,在 net/http 的既定框架下,gin 所做的是提供了一個 gin.Engine 對象作為 Handler 注入其中,從而實現路由注冊/匹配、請求處理鏈路的優化。

我們通過一個 簡化版 gin進行學習核心思想,示例代碼:

func testMiddle(c *gin.Context) {fmt.Println("middle test")
}func main() {// 構造默認配置的 gin.Engineengine := gin.Default()// 注冊中間件engine.Use(testMiddle)// 注冊方法engine.Handle("GET", "/test", func(c *gin.Context) {fmt.Println("route test")})// 啟動 http serverif err := engine.Run(); err != nil {fmt.Println(err)}
}

主要做了幾件事:

  1. 構造默認配置的 gin.Engine
  2. 注冊中間件
  3. 注冊方法
  4. 啟動 http server

gin 是如何與 net/http 鏈接起來的呢?

  1. 路由注冊與查找:gin 的核心結構體 Engine 即實現了該接口:
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {engine.handleHTTPRequest(c)
}
  1. 服務啟動:通過 Engine.Run() 啟動 http server 的背后其實是通過 http.ListenAndServe() 啟動
func (engine *Engine) Run(addr ...string) (err error) {address := resolveAddress(addr)debugPrint("Listening and serving HTTP on %s\n", address)err = http.ListenAndServe(address, engine.Handler())return
}

至此,整個文章已經實現了閉環,更能夠學習到連接的核心思想。


參考:

[1]: https://zhuanlan.zhihu.com/p/609258171 Golang HTTP 標準庫實現原理
[2]: https://astro.yufengbiji.com/posts/golang/ Golang net/http
[3]: https://zhuanlan.zhihu.com/p/611116090 解析 Gin 框架底層原理
[4]: https://blog.csdn.net/weixin_45177370/article/details/135295839?spm=1001.2014.3001.5501 Gin 源碼深度解析及實現

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

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

相關文章

【數據庫初階】SQL--DCL

文章目錄 DCL1. 基本介紹2. 用戶管理2.1 查詢用戶2.2 創建用戶2.3 修改用戶密碼2.4 刪除用戶 3. 權限控制3.1 查詢權限3.2 授予權限3.3 撤銷權限 4. DCL總結 DCL 更多數據庫MySQL系統內容就在以下專欄&#xff1a; 專欄鏈接&#xff1a;數據庫MySQL 1. 基本介紹 DCL英文全稱是…

45-3 護網溯源 - 為什么要做溯源工作

官網:CVERC-國家計算機病毒應急處理中心 西工大遭網絡攻擊再曝細節!13名攻擊者身份查明→ (baidu.com) 護網溯源是指通過技術手段追蹤網絡攻擊的來源和行為,其重要性體現在以下幾個方面: 安全防御:了解攻擊源頭可以幫助組織加強網絡安全防御,及時采取措施防止攻擊的再次…

NXP i.MX8系列平臺開發講解 - 3.14 Linux 之Power Supply子系統(二)

專欄文章目錄傳送門&#xff1a;返回專欄目錄 Hi, 我是你們的老朋友&#xff0c;主要專注于嵌入式軟件開發&#xff0c;有興趣不要忘記點擊關注【碼思途遠】 目錄 1. 前言 2. 芯片簡介 2. 系統原理設計 2. 設備樹相關 本文實操是基于Android11 系統下i.MX8MQ環境下&#x…

基礎復習---二進制補碼

1.二進制補碼表示法基于以下原理&#xff1a; 正數的補碼&#xff1a;正數的補碼與其原碼&#xff08;即直接表示的二進制形式&#xff09;相同。 負數的補碼&#xff1a;負數的補碼是其絕對值的二進制表示&#xff08;即正數的補碼&#xff09;&#xff0c;然后取反&#xff…

代碼隨想錄算法訓練營第十三天| 102. 二叉樹的層序遍歷、226.翻轉二叉樹、101. 對稱二叉樹

102. 二叉樹的層序遍歷 題目鏈接&#xff1a;102. 二叉樹的層序遍歷 文檔講解&#xff1a;代碼隨想錄 狀態&#xff1a;dfs沒寫出來&#xff0c;bfs不知道如何分層 import java.util.*;public class BinaryTreeLevelOrderTraversal {// 用于存儲每一層的節點值List<List<…

rocketmq No route info of this topic 問題排查

Broker配置項 autoCreateTopicEnable true 如果是單節點(master),注釋掉這里的配置 #有三個值&#xff1a;SYNC_MASTER&#xff0c;ASYNC_MASTER&#xff0c;SLAVE&#xff1b;同步和異步表示Master和Slave之間同步數據的機制&#xff1b; #brokerRole SYNC_MASTER Pytho…

【2024最新華為OD-C/D卷試題匯總】[支持在線評測] 土地分配 (100分) - 三語言AC題解(Python/Java/Cpp)

?? 大家好這里是清隆學長 ,一枚熱愛算法的程序員 ? 本系列打算持續跟新華為OD-C/D卷的三語言AC題解 ?? ACM銀牌??| 多次AK大廠筆試 | 編程一對一輔導 ?? 感謝大家的訂閱? 和 喜歡?? ??在線評測鏈接 土地分配(100分) ?? 評測功能需要訂閱專欄后私信聯系清隆解…

阿里云盤手機批量修改文件名

背景 目前刷劇都會使用阿里云盤配合infuse,但是從網上找到的資源經常命名不符合Infuse的刮削規范,導致無法正確建立元數據,雖然PC端可以使用油猴腳本批量修改電視劇名稱, 但是經常出現身邊并沒有電腦(或者電腦上沒有油猴腳本)的情況,這時候用手機就很難批量修改文件名,雖然iph…

Etcd Raft架構設計和源碼剖析2:數據流

Etcd Raft架構設計和源碼剖析2&#xff1a;數據流 | Go語言充電站 前言 之前看到一幅描述etcd raft的流程圖&#xff0c;感覺非常直觀&#xff0c;但和自己看源碼的又有些不同&#xff0c;所以自己模仿著畫了一下&#xff0c;再介紹一下。 下圖從左到右依次分為4個部分&…

游戲心理學Day05

第三章 游戲即學習 《超級馬里奧》是游戲史上的經典之作&#xff0c;我們都記得第一次踩到敵人&#xff0c;第一次頂碎磚塊時的快樂&#xff0c;也記得為了通過某個關卡而付出的努力和艱辛。當我們掌握了規律和技巧之后&#xff0c;這些難題就不再是難題&#xff0c;因為我們習…

Windows 宿主機訪問 VirtualBox 虛擬機中創建的 docker 容器中的 mysql8.0 的數據

一、場景需求 在開發環境中&#xff0c;一般使用 windows 系統進行開發&#xff0c;但需要在 linux 系統中創建運行 mysql8.0 的 docker 容器中進行測試&#xff08;win10特定版本或win11才能安裝 docker&#xff09;&#xff0c;為了方便還需要在 windows 系統中通過 SQLyog …

植物大戰僵尸雜交版2.0.88最新版+防閃退工具V2+修改工具+高清工具

植物大戰僵尸雜交版&#xff0c;不僅繼承原作的經典玩法&#xff0c;而且引入了全新的植物融合玩法&#xff0c;將各式各樣的植物進行巧妙的雜交&#xff0c;孕育出前所未有、功能各異的全新植物。 創新的雜交合成系統 游戲引入了創新的雜交合成系統&#xff0c;讓玩家可以將不…

Unity DOTS技術(五)Archetype,Chunk,NativeArray

文章目錄 一.Chunk和Archetype什么是Chunk?什么是ArchType 二.Archetype創建1.創建實體2.創建并添加組件3.批量創建 三.多線程數組NativeArray 本次介紹的內容如下: 一.Chunk和Archetype 什么是Chunk? Chunk是一個空間,ECS系統會將相同類型的實體放在Chunk中.當一個Chunk…

JS包裝類:循環中為什么建議用變量存儲str.length進行循環判斷?

前言 在Javascript通常我們在遍歷一個字符串的時候通常使用的方式是 var str "abcdefg"; for(let i0;i<str.length;i){}但在最近的學習中&#xff0c;有人建議我最好應該是下面這樣執行。 var str "abcdefg"; for(let i0,len str.length;i<len;i)…

DP讀書:《ModelArts人工智能應用開發指南》(一)人工智能技術、應用平臺

怎么用ModelArts人工智能應用 訓練底座訓練案例 盤古礦山模型Main config.py 訓練底座 訓練案例 盤古礦山模型 Main 下面是快速助手 https://support.huaweicloud.com/qs-modelarts/modelarts_06_0006.html 準備開發環境 在ModelArts控制臺的“ 開發環境 > Notebook”頁面…

【C#學習筆記】屬性和字段

文章目錄 前言屬性和字段的區別字段訪問修飾符和關鍵字定義變量類型的定義變量命名變量的賦值 屬性 不同的使用情況 前言 最近在工作的過程中常常會覺得自己在程序設計方面的能力還是有欠缺。例如一直對于變量的聲明感到不足&#xff0c;在工作中為了圖方便總是直接public定義…

聲音突破:so 索

小孩兒看完武俠劇&#xff0c;就決定從二樓往地面上跳&#xff0c;年輕的老媽看到了&#xff0c;就在那里罵&#xff0c;喝斥不準逞能&#xff0c;不許亂來&#xff0c;不許跳。但小孩子不聽話&#xff0c;心里全是影視劇的畫面&#xff0c;那叫一個俠之能也&#xff0c;于是飛…

llvm 常用命令備忘

執行 IR 上的指令合并優化 pass $ opt –S –instcombine testfile.ll –o output1.ll 執行無效參數優化 pass $ opt –S –deadargelim testfile.ll –o output2.ll C 語言生成 IR 文件 $ clang -emit-llvm -S multiply.c -o multiply.ll C 語言生成 IR 文件 $ clang -cc1 -…

面向長文本處理的鍵值緩存壓縮技術:智能壓縮,無損性能,免微調

隨著輸入長度的增加&#xff0c;大型語言模型&#xff08;LLMs&#xff09;中的鍵值&#xff08;KV&#xff09;緩存需要存儲更多的上下文信息以維持性能&#xff0c;這導致內存消耗和計算時間急劇上升。KV緩存的增長對內存和時間效率的挑戰主要表現在兩個方面&#xff1a;一是…