golang錯誤處理

jopen 8年前發布 | 31K 次閱讀 Go語言 Google Go/Golang開發

golang錯誤處理方式一直是很多人詬病的地方,很多人都吐槽說一半的代碼是 if err != nil { / 錯誤處理 / },嚴重影響正常的處理邏輯,我最開啟也反感這種錯誤處理機制,每調用完一個函數都需要check一下,自定義函數也都要增加一個error類型的返回值,但是查了很多資料慢慢理解這種錯誤機制的好處。因為調用每一個函數都可能發生錯誤,及時在錯誤發生的地方做處理更容易構建復雜的大型系統。

異常與錯誤

錯誤與異常是很容易混淆的地方,錯誤指的是可能出現問題的地方出現了問題,比如打開一個文件時失敗,這種情況在人們的意料之中;而異常指的是不應該出現問題的地方出現了問題,這種情況在人們的意料之外。golang使用 panic,defer,recover 來處理異常情況,類似java中的try catch finially。 使用panic拋出異常,拋出異常后將立即停止當前函數的執行并運行所有被defer的函數,然后將panic拋向上一層,直至程序carsh。但是也可以使用被defer的recover函數來捕獲異常阻止程序的崩潰,recover只有被defer后才是有意義的。 ``` //當一個http鏈接到來時,golang會調用serve函數,serve函數會解析http協議,然后交給上層的handler處理,如果上層的handler內拋出異常的話,會被defer里面的recover函數捕獲,打印出當前的堆棧信息,這樣就可以防止一個http請求發生異常導致整個程序崩潰了。

func (c *conn)serve() { defer func() { if err := recover(); err != nil { const size = 64<<10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] fmt.Printlf("http: panic serving %v: %v\\%s", c.remoteAddr, err, buf) } }() w := c.readRequest() ServeHTTP(w, w.req) }```

錯誤處理

我們也可以使用一些方法來簡化錯誤處理的邏輯,總體思想是將重復的代碼復用。

  • 在defer中集中處理錯誤``` func demo() { var err error defer func() { if err != nil { // 處理發生的錯誤,打印日志等信息 return } }

    err = func1() if err != nil { return }

    err = func2() if err != nil { return } ..... } //如果golang支持宏,將能if err != nil { return }作為一個宏定義就完美了,可惜golang不支持宏。 ```

  • all-or-nothing check這個是Rob Pike在 Errors rea values 這篇文章中提到的方法,如果一個完整的任務內有多個小的任務,我們沒有必要執行完每個小的任務都檢查下是否有錯,我們可以在所有的小任務執行完成后再去判斷整個任務的執行過程中有沒有錯誤。 type errWriter struct { w io.Writer err error } func (ew *errWriter) write(buf []byte) { if ew.err != nil { return } _, ew.err = ew.w.Write(buf) } ew := &errWriter{w: fd} ew.write(p0[a:b]) ew.write(p1[c:d]) ew.write(p2[e:f]) // 只需要在最后檢查一次 if ew.err != nil { return ew.err } //這種處理方式有一個明顯的問題是不知道整個任務是在哪一個小的任務執行的時候發生錯誤,如果我們需要關注每個小任務的進度則這種方式就不適合了。 ```

    錯誤上下文

    error是一個接口,任何實現了Error()函數的類型都滿足該接口,errors.New()實際上返回的是一個errorString類型,該類型內僅包含一個string字符串,沒有錯誤發生的上下文信息,當我們把error不斷向上拋,在上層做統一處理時,只能輸出一個字符串信息而不知道錯誤是在什么地方什么情況下發生的。 ``` type error interface { Error() string }

type errorString struct { s string }

func (e *errorString) Error() string { return e.s }

func New(text string) error { return &errorString{text} } ` package stackerr

import ( "fmt" "runtime" "strings" )

type StackErr struct { Filename string CallingMethod string Line int ErrorMessage string StackTrace string }

func New(err interface{}) StackErr { var errMessage string switch t := err.(type) { case StackErr: return t case string: errMessage = t case error: errMessage = t.Error() default: errMessage = fmt.Sprintf("%v", t) } stackErr := &StackErr{}

}

func (this *StackErr) Error() string { return this.ErrorMessage }

func (this *StackErr) Stack() string { return fmt.Sprintf("{%s:%d} %s\nStack Info:\n %s", this.Filename, this.Line, this.ErrorMessage, this.StackTrace) }

func (this *StackErr) Detail() string { return fmt.Sprintf("{%s:%d} %s", this.Filename, this.Line, this.ErrorMessage) }

```

參考文獻 Error handling and Go 《Go web編程》--11.1 錯誤處理 Go語言錯誤處理 (Tony Bai的博客) Errors are values StackErr項目源碼

來自: http://shanks.leanote.com/post/0bd5b756ab7c

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!