介紹
Gin 是一個用 Go(Golang)寫的 HTTP Web 框架。它具有類似 Martini 的 API,但效能比 Martini 快 40 倍。如果您需要精彩的表演,那就給自己點杜松子酒吧。
Gin 官網介紹自己是一個具有「高效能」和「良好生產力」的 Web 框架。它還提到了另外兩個庫。第一個是Martini,它也是一個Web框架,並且有一個酒的名字。 Gin 表示它使用其 API,但速度快了 40 倍。使用httprouter是它比Martini快40倍的重要原因。
官網的「Features」中列出了八個關鍵功能,稍後我們將逐步看到這些功能的實現。
- 快
- 中介軟體支援
- 無崩潰
- JSON 驗證
- 路線分組
- 錯誤管理
- 渲染內建/可擴充
從一個小例子開始
讓我們來看看官方文件中給出的最小範例。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
執行這個例子,然後用瀏覽器存取http://localhost:8080/ping,就會得到「乒乓」聲。
這個例子非常簡單。它可以分為三個步驟:
- 使用 gin.Default() 建立具有預設配置的 Engine 物件。
- 在Engine的GET方法中註冊「/ping」位址的回呼函數。此函數將傳回一個“pong”。
- 啟動Engine,開始監聽連接埠並提供服務。
HTTP方法
從上面小例子中的GET方法我們可以看出,在Gin中,HTTP方法的處理方法需要使用對應的同名函數來註冊。
HTTP 方法有九種,最常用的四種是 GET、POST、PUT 和 DELETE,分別對應查詢、插入、更新和刪除四種功能。需要注意的是,Gin還提供了Any接口,可以直接將所有HTTP方法處理方法綁定到一個位址。
傳回的結果一般包含兩部分或三部分。代碼和訊息始終存在,資料通常用於表示附加資料。如果沒有額外的資料要返回,則可以省略。在範例中,200 是 code 欄位的值,「pong」是 message 欄位的值。
創建引擎變數
在上面的範例中,gin.Default() 用來建立引擎。然而,這個函數是 New 的包裝。其實Engine就是透過New介面創建的。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
暫時簡單看一下建立過程,不要注意Engine結構體中各個成員變數的意義。可以看到,New除了建立並初始化一個Engine類型的引擎變數外,還將engine.pool.New設定為一個呼叫engine.allocateContext()的匿名函數。這個函數的作用後面會講。
註冊路由回呼函數
Engine 中有一個嵌入的 struct RouterGroup。 Engine的HTTP方法相關介面皆繼承自RouterGroup。官網提到的功能點中的「路由分組」是透過RouterGroup結構體實現的。
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ //... Initialize the fields of RouterGroup }, //... Initialize the remaining fields } engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup engine.pool.New = func() any { return engine.allocateContext() } return engine }
每個 RouterGroup 都與一個基本路徑 basePath 相關聯。 Engine 中嵌入的 RouterGroup 的基本路徑是「/」。
還有一組處理函數Handlers。所有與該群組關聯的路徑下的請求都會額外執行該群組的處理函數,主要用於中間件呼叫。 Engine建立時Handlers為nil,可以透過Use方法匯入一組函數。我們稍後會看到這個用法。
type RouterGroup struct { Handlers HandlersChain // Processing functions of the group itself basePath string // Associated base path engine *Engine // Save the associated engine object root bool // root flag, only the one created by default in Engine is true }
RouterGroup的handle方法是註冊所有HTTP方法回呼函數的最終入口。初始範例中呼叫的 GET 方法和其他與 HTTP 方法相關的方法只是對 handle 方法的包裝。
handle方法會根據RouterGroup的basePath和相對路徑參數計算出絕對路徑,同時呼叫combineHandlers方法得到最終的handlers陣列。這些結果會作為參數傳遞給Engine的addRoute方法來註冊處理函數。
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() }
combineHandlers方法的作用是建立一個切片mergedHandlers,然後將RouterGroup本身的Handler複製到其中,然後將參數的handler複製到其中,最後傳回mergedHandlers。也就是說,當使用handle註冊任何方法時,實際的結果都包含了RouterGroup本身的Handler。
使用Radix Tree加速路由檢索
在官網提到的「快速」特徵點中,提到網路請求的路由是基於基數樹(Radix Tree)實現的。這部分並不是Gin實現的,而是由一開始介紹Gin時提到的httprouter實現的。 Gin使用httprouter來實現這部分功能。基數樹的實現這裡暫時不講。我們現在只關注它的用法。也許稍後我們會單獨寫一篇文章來介紹基數樹的實作。
在引擎中,有一個 trees 變量,它是 methodTree 結構的切片。正是這個變數保存了對所有基數樹的引用。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
引擎為每個 HTTP 方法維護一個基數樹。這棵樹的根節點和方法的名稱一起保存在一個methodTree變數中,所有methodTree變數都在樹中。
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ //... Initialize the fields of RouterGroup }, //... Initialize the remaining fields } engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup engine.pool.New = func() any { return engine.allocateContext() } return engine }
可以看到,在Engine的addRoute方法中,首先會使用trees的get方法來取得此方法對應的radix樹的根節點。如果沒有取得到基數樹的根節點,則表示先前沒有為該方法註冊過任何方法,將會建立一個樹節點作為樹的根節點,並加入到樹中。
取得根節點後,使用根節點的addRoute方法註冊一組針對路徑path的處理函數handler。這一步驟是為路徑和處理程序建立一個節點並將其儲存在基數樹中。如果你嘗試註冊一個已經註冊的地址,addRoute會直接拋出一個panic錯誤。
在處理HTTP請求時,需要透過路徑找到對應節點的值。根節點有一個getValue方法負責處理查詢作業。我們在談論 Gin 處理 HTTP 請求時會提到這一點。
導入中間件處理函數
RouterGroup的Use方法可以匯入一組中間件處理函數。官網提到的功能點中的「中間件支援」是透過Use方法實現的。
在最初的範例中,在建立Engine結構體變數時,沒有使用New,而是使用了Default。讓我們看看 Default 額外做了什麼。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
可以看出,這是一個非常簡單的函數。除了呼叫New建立Engine物件外,只呼叫Use導入Logger和Recovery兩個中間件函數的回傳值。 Logger的傳回值是用來記錄日誌的函數,Recovery的傳回值是用來處理panic的函數。我們暫時跳過這個,稍後再看這兩個函數。
雖然Engine內嵌了RouterGroup,也實作了Use方法,但只是呼叫了RouterGroup的Use方法以及一些輔助操作。
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ //... Initialize the fields of RouterGroup }, //... Initialize the remaining fields } engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup engine.pool.New = func() any { return engine.allocateContext() } return engine }
可見RouterGroup的使用方法也非常簡單。它只是透過append將參數的中間件處理功能添加到自己的Handler中。
開始跑步
在這個小例子中,最後一步就是不帶參數呼叫 Engine 的 Run 方法。呼叫後,整個框架開始運行,用瀏覽器存取註冊地址即可正確觸發回調。
type RouterGroup struct { Handlers HandlersChain // Processing functions of the group itself basePath string // Associated base path engine *Engine // Save the associated engine object root bool // root flag, only the one created by default in Engine is true }
Run方法只做兩件事:解析位址和啟動服務。這裡地址其實只要傳一個字串就可以了,但為了達到能傳能不能傳的效果,使用了一個可變參數。 resolveAddress方法處理addr不同情況的結果。
啟動服務使用標準庫的net/http套件中的ListenAndServe方法。此方法接受一個監聽位址和一個Handler介面的變數。 Handler介面的定義非常簡單,只有一個ServeHTTP方法。
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() }
因為Engine實作了ServeHTTP,所以Engine本身將會被傳遞到這裡的ListenAndServe方法。當監聽埠有新的連接時,ListenAndServe會負責接受並建立連接,當連接上有資料時,會呼叫handler的ServeHTTP方法進行處理。
處理訊息
Engine的ServeHTTP是處理訊息的回呼函數。讓我們來看看它的內容。
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain { finalSize := len(group.Handlers) + len(handlers) assert1(finalSize回呼函數有兩個參數。第一個是w,用於接收請求回覆。將回覆資料寫入w。另一個是req,保存本次請求的資料。後續處理所需的所有資料都可以從req讀取
ServeHTTP 方法做了四件事。首先從pool池取得一個Context,然後將Context與回呼函數的參數綁定,然後以Context為參數呼叫handleHTTPRequest方法來處理這次網路請求,最後將Context放回池中。
我們首先只看handleHTTPRequest方法的核心部分。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }handleHTTPRequest方法主要做了兩件事。首先根據請求的位址從基數樹中取得先前註冊的方法。這裡,handlers會被指派到Context中進行本次處理,然後呼叫Context的Next函數來執行handlers中的方法。最後將本次請求的回傳資料寫入Context的responseWriter類型物件中。
情境
處理 HTTP 請求時,所有與上下文相關的資料都在 Context 變數中。作者也在Context結構體的註釋中寫到“Context is the most important part of gin”,可見其重要性。
上面講Engine的ServeHTTP方法時可以看出,Context並不是直接建立的,而是透過Engine的pool變數的Get方法取得的。取出後,使用前重置其狀態,使用後放回池中。
Engine 的池變數的類型為sync.Pool。目前只知道它是Go官方提供的支援並發使用的物件池。您可以透過 Get 方法從池中取得對象,也可以使用 Put 方法將物件放入池中。當池為空並且使用Get方法時,它會透過自己的New方法建立物件並傳回。
這個New方法是在Engine的New方法中定義的。我們再看一下Engine的New方法。
func New() *Engine { debugPrintWARNINGNew() engine := &Engine{ RouterGroup: RouterGroup{ //... Initialize the fields of RouterGroup }, //... Initialize the remaining fields } engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup engine.pool.New = func() any { return engine.allocateContext() } return engine }從程式碼可以看出Context的建立方法是Engine的allocateContext方法。 allocateContext 方法並沒有什麼神秘之處。它只是對切片長度進行兩步預分配,然後創建物件並返回它。
type RouterGroup struct { Handlers HandlersChain // Processing functions of the group itself basePath string // Associated base path engine *Engine // Save the associated engine object root bool // root flag, only the one created by default in Engine is true }上面提到的 Context 的 Next 方法將執行處理程序中的所有方法。讓我們來看看它的實現。
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj() }雖然handlers是一個切片,但是Next方法並不是簡單地實現為handlers的遍歷,而是引入了一個處理進度記錄索引,該索引初始化為0,在方法開始時遞增,在方法結束後再次遞增執行完成。
Next的設計和它的用法有很大關係,主要是為了配合一些中間件功能。例如,當某個handler執行過程中觸發panic時,可以使用中間件中的recover捕獲錯誤,然後再次呼叫Next繼續執行後續的handler,而不會因為該問題影響整個handlers數組一名處理程序。
應對恐慌
在Gin中,如果某個請求的處理函數觸發了panic,整個框架並不會直接崩潰。相反,將拋出錯誤訊息,並將繼續提供服務。這有點類似Lua框架通常使用xpcall來執行訊息處理函數。這個操作就是官方文件中提到的「Crash-free」特性點。
上述提到,使用 gin.Default 建立 Engine 時,會執行 Engine 的 Use 方法來匯入兩個函數。其中之一是 Recovery 函數的傳回值,它是其他函數的包裝。最終呼叫的函數是CustomRecoveryWithWriter。我們來看看這個函數的實作。
package main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }這裡我們不關注錯誤處理的細節,而只看看它做了什麼。該函數傳回一個匿名函數。在這個匿名函數中,使用defer註冊了另一個匿名函數。在這個內部匿名函式中,使用recover來捕捉panic,然後進行錯誤處理。處理完成後,呼叫Context的Next方法,這樣Context原本依序執行的處理程序就可以繼續執行。
Leapcell:用於 Web 託管、非同步任務和 Redis 的下一代無伺服器平台
最後跟大家介紹一下部署Gin服務最好的平台:Leapcell。
1. 多語言支持
- 使用 JavaScript、Python、Go 或 Rust 進行開發。
2.免費部署無限個項目
- 只需支付使用費用-無請求,不收費。
3. 無與倫比的成本效益
- 即用即付,無閒置費用。
- 範例:25 美元支援 694 萬個請求,平均回應時間為 60 毫秒。
4.簡化的開發者體驗
- 直覺的使用者介面,輕鬆設定。
- 完全自動化的 CI/CD 管道和 GitOps 整合。
- 即時指標和日誌記錄以獲取可操作的見解。
5. 輕鬆的可擴充性和高效能
- 自動擴充以輕鬆處理高並發。
- 零營運開銷-只需專注於建置。
在文件中探索更多內容!
Leapcell Twitter:https://x.com/LeapcellHQ
以上是深入探討 Gin:Golang 的領先框架的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文演示了創建模擬和存根進行單元測試。 它強調使用接口,提供模擬實現的示例,並討論最佳實踐,例如保持模擬集中並使用斷言庫。 文章

本文探討了GO的仿製藥自定義類型約束。 它詳細介紹了界面如何定義通用功能的最低類型要求,從而改善了類型的安全性和代碼可重複使用性。 本文還討論了局限性和最佳實踐

本文討論了GO的反思軟件包,用於運行時操作代碼,對序列化,通用編程等有益。它警告性能成本,例如較慢的執行和更高的內存使用,建議明智的使用和最佳

本文討論了GO中使用表驅動的測試,該方法使用測試用例表來測試具有多個輸入和結果的功能。它突出了諸如提高的可讀性,降低重複,可伸縮性,一致性和A

OpenSSL,作為廣泛應用於安全通信的開源庫,提供了加密算法、密鑰和證書管理等功能。然而,其歷史版本中存在一些已知安全漏洞,其中一些危害極大。本文將重點介紹Debian系統中OpenSSL的常見漏洞及應對措施。 DebianOpenSSL已知漏洞:OpenSSL曾出現過多個嚴重漏洞,例如:心臟出血漏洞(CVE-2014-0160):該漏洞影響OpenSSL1.0.1至1.0.1f以及1.0.2至1.0.2beta版本。攻擊者可利用此漏洞未經授權讀取服務器上的敏感信息,包括加密密鑰等。

本文使用跟踪工具探討了GO應用程序執行流。 它討論了手冊和自動儀器技術,比較諸如Jaeger,Zipkin和Opentelemetry之類的工具,並突出顯示有效的數據可視化


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SublimeText3 Linux新版
SublimeText3 Linux最新版

PhpStorm Mac 版本
最新(2018.2.1 )專業的PHP整合開發工具