Golang Gin应用中如何捕获Goroutine内Panic并返回500错误?
Gin中处理异步Goroutine panic并返回HTTP 500的方案
在Gin应用里,异步Goroutine的panic不会自动传递到处理请求的主Goroutine,而且Gin的上下文(*gin.Context)不是并发安全的,不能直接在子Goroutine里调用响应方法。要实现"任意一个Calculate() panic就返回500"的需求,核心思路是用错误通道捕获panic,在主Goroutine里监听错误并及时返回响应。
具体实现步骤
- 为计算结果和错误分别创建通道,错误通道要设置足够缓冲避免Goroutine阻塞泄漏
- 给每个执行
Calculate()的Goroutine添加defer recover()逻辑,将panic转为错误发送到错误通道 - 主Goroutine通过
select同时监听结果和错误通道,一旦收到错误立即返回500状态码;所有计算正常完成则返回结果
完整代码示例
package main import ( "fmt" "net/http" "github.com/gin-gonic/gin" ) // 假设的物流选项结构体 type LogisticsOption struct { Name string `json:"name"` Price float64 `json:"price"` } // 模拟可能panic的计算函数 func CalculateOption1() (LogisticsOption, error) { // 这里可以模拟panic场景,比如:panic("divide by zero") return LogisticsOption{Name: "顺丰速运", Price: 23.5}, nil } func CalculateOption2() (LogisticsOption, error) { // 模拟panic // panic("database connection failed") return LogisticsOption{Name: "京东物流", Price: 19.9}, nil } func LogisticsHandler(c *gin.Context) { // 定义结果通道和错误通道,缓冲数等于Goroutine数量 resChan := make(chan LogisticsOption, 2) errChan := make(chan error, 2) // 封装带panic捕获的计算逻辑 safeRun := func(calcFunc func() (LogisticsOption, error)) { defer func() { if r := recover(); r != nil { // 将panic转为可传递的错误 errChan <- fmt.Errorf("calculation failed unexpectedly: %v", r) } }() opt, err := calcFunc() if err != nil { errChan <- err return } resChan <- opt } // 启动两个异步计算Goroutine go safeRun(CalculateOption1) go safeRun(CalculateOption2) // 收集结果或处理错误 var results []LogisticsOption for i := 0; i < 2; i++ { select { case err := <-errChan: // 任意一个计算出错(包括panic),立即返回500 c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) // 关闭通道避免Goroutine泄漏 close(resChan) close(errChan) return case res := <-resChan: results = append(results, res) } } // 所有计算正常完成,返回结果 c.JSON(http.StatusOK, gin.H{"logistics_options": results}) } func main() { r := gin.Default() r.GET("/logistics", LogisticsHandler) r.Run(":8080") }
关键注意事项
- 错误通道必须设置缓冲,否则当panic发生时,子Goroutine发送错误会阻塞,导致Goroutine泄漏
- 绝对不能在子Goroutine中直接操作
*gin.Context,所有响应逻辑必须回到主请求Goroutine处理 - 一旦返回错误,要主动关闭剩余通道,避免未完成的Goroutine一直阻塞等待发送数据
内容的提问来源于stack exchange,提问作者Smirnov
相关产品推荐
相关产品推荐

