Day7:错误恢复
今天的任务是:
- 实现错误处理机制。
https://i-blog.csdnimg.cn/direct/60d1933ac59c4180880533d400661ef7.jpeg#pic_center" alt="请添加图片描述" />
panic
在 Golang 中,较为常见的错误处理方式是返回 error,由调用者决定后续如何处理。但如果是无法恢复的错误,可以手动触发 panic,当然如果在程序运行时出现类似于数组越界的错误,panic 也会触发。panic 会终止当前程序的执行并退出。
defer
panic 会导致程序被终止,但是在退出前,会先处理完当前协程(注意,main 函数本身也是一个协程,可以理解为它是主协程)上已经 defer 的任务,执行完后再退出。
可以 defer 多个任务,在同一个函数中 defer 多个任务时,会逆序执行,即先执行最后 defer 的任务。
defer 的任务执行完成后,panic 会继续抛出,导致程序非正常结束。
recover
Golang 提供了内置的 recover 函数,可以避免因为 panic 导致的整个程序终止,recover 只在 defer 中生效。
Gee 的错误处理机制
我们在第六天实现的框架中没有加入异常处理机制,如果代码中存在会触发 panic 的 bug,程序很容易宕机。
今天我们在 gee 中添加一个非常简单的错误处理机制,即在此类型错误发生时,向用户返回 Internal Server Error,并在日志中打印必要的错误信息,方便进行错误定位。
我们之前已经实现了中间件机制,错误处理可以作为一个中间件,增强 gee 的能力。
新增 gee/recovery.go
,在这个文件中实现中间件 Recovery:
// print stack trace for debug
func trace(message string) string {var pcs [32]uintptrn := runtime.Callers(3, pcs[:])var str strings.Builderstr.WriteString(message + "\nTraceback:")for _, pc := range pcs[:n] {fn := runtime.FuncForPC(pc)file, line := fn.FileLine(pc)str.WriteString(fmt.Sprintf("\n\t%s:%d", file, line))}return str.String()
}func Recovery() HandlerFunc {return func(c *Context) {defer func() {if err := recover(); err != nil {message := fmt.Sprintf("%s", err)log.Printf("%s\n\n", trace(message))c.Fail(http.StatusInternalServerError, "Internal Server Error")}}()c.Next()}
}
其中的 trace()
函数用于触发 panic 的堆栈信息。至此,Gee 的错误处理机制已经完成。
Demo
package mainimport ("gee/gee""net/http"
)func main() {r := gee.New()r.Use(gee.Logger(), gee.Recovery()) // 使用 Recovery 中间件r.GET("/", func(c *gee.Context) {c.String(http.StatusOK, "Hello Geektutu\n")})// index out of range for testing Recovery()r.GET("/panic", func(c *gee.Context) {names := []string{"geektutu"}c.String(http.StatusOK, names[100])})r.Run(":9999")
}
至此,我们完成了 Geektutu 大佬的 Gee Web 框架教程,并实现了完整的 Web 框架 Gee。简单回顾一下,Gee 使用 Context 封装了 http 的 ResponseWriter 和 Request【Web 服务要做的就是根据 Request 生成相应的 Response,并通过 ResponseWriter 返回给用户】,并使用 router 来实现动态路由【基于 Trie 树实现】,在 router 的基础上,进一步加入了 Group 使得路由可以被分组。此外,Gee 支持用户自定义的中间件,并在理论上可以无限拓展。中间件的行为类似于一个 HandlerFunc,它们会按照顺序执行非业务的逻辑,比如错误恢复或日志记录。