gin 框架是如何處理 panic 的?

大家好,我是漁夫子

本文我們介紹下 recover 在 gin 框架中的應用。首先,在 golang 中,如果在子協程中遇到了 panic,那麼主協程也會被終止。如下:

package main

import (
 "github.com/gin-gonic/gin"
)

func main() {
 r := gin.Default()

    // 在子協程中引起panic,主協程也會退出
 go func() {
  panic("hello world")
 }()
    
 // Listen and Server in 0.0.0.0:8080
 r.Run(":8080")
}

panic 被描述爲不可處理的錯誤。在 web 服務中就是服務會崩潰。當然,這在生產環境下是不可接受的。那麼,如何能夠做到發生 panic 時技能捕獲該 panic 又能讓服務繼續健康運行呢?

這就是 golang 中提供的 recover 函數了。recover 函數能夠捕獲 Panic 錯誤並恢復程序的正常運行。接下來,我們看下 recover 函數在 gin 框架中是如何應用的。

首先,要提到的就是 gin 框架中的 recovery 中間件。在 gin 中,是通過使用該中間件來捕獲 panic,並保證服務不 down 機的。如果使用 gin.Default() 函數進行構建 gin 對象,那麼默認就註冊了 Recovery 中間件。

func Default() *Engine {
 debugPrintWARNINGDefault()
 engine := New()
    //  註冊了Recovery中間件
 engine.Use(Logger(), Recovery())
 return engine
}

其次,我們來看下 Recovery() 中間件都做了些什麼。

Recovery() 函數定義如下:

func Recovery() HandlerFunc {
 return RecoveryWithWriter(DefaultErrorWriter)
}

這裏的 DefaultErrorWriter 是默認的輸出端,即 os.Stderr。即指錯誤的輸出到什麼地方。

接下來看 RecoveryWithWriter 函數中的實現

// RecoveryWithWriter returns a middleware for a given writer that recovers from any panics and writes a 500 if there was one.
func RecoveryWithWriter(out io.Writer, recovery ...RecoveryFunc) HandlerFunc {
 if len(recovery) > 0 {
  return CustomRecoveryWithWriter(out, recovery[0])
 }
 return CustomRecoveryWithWriter(out, defaultHandleRecovery)
}

這裏有一個參數是 defaultHandleRecovery,我們看下它的實現:

func defaultHandleRecovery(c *Context, err any) {
 c.AbortWithStatus(http.StatusInternalServerError)
}

就是寫入了一個代表內部服務器錯誤的狀態碼 500,並結束了本次請求。

這裏關鍵點是 CustomRecoveryWithWriter 的實現,代碼很長,我們分段來看。如下:主要分三部分:

這裏需要注意的點就是:

在 gin 中,正是該中間件的應用,確保了 web 服務的健壯性。當然,其他的 web 框架也有同樣的機制,實現原理也是一樣的。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/SCefvDhQR3nq-xU54fZ41g