本文我们介绍下recover在gin框架中的应用。首先,在golang中,如果在子协程中遇到了panic,那么主协程也会被终止。如下:
package mainimport (
"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框架也有同样的机制,实现原理也是一样的。
推荐阅读