隨筆: Golang 錯誤處理

  • 值得參考的文章:
  • recover
    • gin中如果使用goroutine這類async邏輯,必須補充recover的邏輯,才可捕捉錯誤避免崩潰。非async邏輯可直接用r.Use(gin.Recovery())捕捉
      • panic發生在非async邏輯中: 如果使用r.Use(gin.Recovery()),發生panic會捕捉到此錯誤,並回傳500給呼叫端

        package main
        
        import (
        	"github.com/gin-gonic/gin"
        )
        
        func main() {
        	r := gin.New()
        	r.Use(gin.Logger())
        	r.Use(gin.Recovery())
        
        	r.GET("/ping", func(c *gin.Context) {
        		func() {
        			panic("panic")
        		}()
        
        		c.JSON(200, gin.H{
        			"message": "pong",
        		})
        	})
        
        	r.Run()
        }
        
      • panice發生在async邏輯中: 雖使用r.Use(gin.Recovery()),但panic發生在goroutine中,則panic不會捕捉,並且程序會崩潰,必須在goroutine補充recover的邏輯,才可捕捉錯誤避免崩潰

        package main
        
        import (
        	"fmt"
        	"sync"
        
        	"github.com/gin-gonic/gin"
        )
        
        type WaitGroupWithError struct {
        	sync.WaitGroup
        	Error error
        }
        
        func (w *WaitGroupWithError) Wait() error {
        	w.WaitGroup.Wait()
        	return w.Error
        }
        
        func main() {
        	r := gin.New()
        	r.Use(gin.Logger())
        	r.Use(gin.Recovery())
        
        	r.GET("/ping", func(c *gin.Context) {
        		var wg WaitGroupWithError
        		wg.Add(1)
        		go func() {
        			defer func() {
        				// 需要在此透過recover捕捉
        				if err := recover(); err != nil {
        					wg.Error = fmt.Errorf("get panic: %v", err)
        				}
        				wg.Done()
        			}()
        			panic("panic")
        		}()
        		if err := wg.Wait(); err != nil {
        			fmt.Printf("get err: %+v", err)
        			c.JSON(500, gin.H{
        				"message": "internal error",
        			})
        			return
        		}
        
        		c.JSON(200, gin.H{
        			"message": "pong",
        		})
        	})
        
        	r.Run()
        }
        
      • 參考:

comments powered by Disqus