主頁 > 知識庫 > 詳解Go語言的錯誤處理和資源管理

詳解Go語言的錯誤處理和資源管理

熱門標簽:阿克蘇地圖標注 壽光微信地圖標注 外呼系統(tǒng)顯本地手機號 電話機器人軟件免費 excel地圖標注分布數(shù)據(jù) 百度地圖標注后傳給手機 涿州代理外呼系統(tǒng) 評價高的400電話辦理 外呼系統(tǒng)用什么卡

一、defer

1. defer保證在函數(shù)結(jié)束時發(fā)生.

2. defer列表為先進后出

3. 參數(shù)在defer語句時計算.

下面來看一個例子: 寫入文件

package main

import (
    "aaa/functional/fbi"
    "bufio"
    "fmt"
    "os"
)

// 我要寫文件
func writeFile() {
    file, err := os.Create("test.txt")
    if err != nil {
        panic("error")
    }
    defer file.Close()

    w := bufio.NewWriter(file)
    defer w.Flush()

    f := fbi.Feibonaccq()
    for i := 0; i  20; i++  {
        fmt.Fprintln(w, f())
    }

}
func main() {
    writeFile()
}
package fbi

func Feibonaccq() func() int {
    x, y := 0, 1
    return func() int {
        x, y = y, x+y
        return x
    }
}

將斐波那契數(shù)列寫入文件. 這里有兩個資源使用. 1. 創(chuàng)建文件, 然后文件關(guān)閉. 2. 寫入資源, 將資源從緩存中刷入文件. 這兩個操作都應該應該是成對出現(xiàn)的, 因此, 用defer 語句, 避免后面寫著寫著忘了, 也保證即使出錯了, 也能夠執(zhí)行defer語句的內(nèi)容

那么參數(shù)在defer語句時計算 是什么意思呢?

func tryDefer() {
    for i := 0; i  10 ; i++ {
        defer fmt.Println(i)
    }
}

打印結(jié)果:

9

8

7

6

5

4

3

2

1

0

二、錯誤處理

所謂的錯誤處理, 就是處理已知的錯誤, 不要拋出panic這樣導致系統(tǒng)掛掉的錯誤發(fā)生.

比如下面的操作:

package main

import (
    "aaa/functional/fbi"
    "bufio"
    "fmt"
    "os"
)

// 我要寫文件
func writeFile(filename string) {
    // os.O_EXCL|os.O_CREATE創(chuàng)建一個新文件, 并且他必須不存在
    file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666)
    // 這時候打印panic就不太友好. 我們可以對錯誤類型進行處理
    /*if err != nil {
        panic("error")
    }*/

    // 這里就對錯誤的類型進行了捕獲處理.
    if err, ok := err.(*os.PathError); !ok {
        fmt.Println("未知錯誤")
    } else {
        fmt.Printf("%s, %s, %s", err.Path, err.Op, err.Err)
    }

    defer file.Close()

    w := bufio.NewWriter(file)
    defer w.Flush()

    f := fbi.Feibonaccq()
    for i := 0; i  20; i++  {
        fmt.Fprintln(w, f())
    }

}
func main() {
    writeFile("test.txt")
}

紅色字體部分就是對錯誤進行了捕獲處理.

三、統(tǒng)一錯誤處理的邏輯

下面模擬一個web服務器, 在瀏覽器地址欄輸入文件的url, 然后顯示文件的內(nèi)容. 比如斐波那契數(shù)列的文件

package main

import (
    "io/ioutil"
    "net/http"
    "os"
)

// 我們來模擬一個web服務器. 在url上輸入一個地址, 然后顯示文件內(nèi)容
// 做一個顯示文件的web server
func main() {
    http.HandleFunc("/list/", func(writer http.ResponseWriter, request *http.Request) {
        // 獲取url路徑, 路徑是/list/之后的部分
        path := request.URL.Path[len("/list/"):]
        // 打開文件
        file, err := os.Open(path)
        if err != nil {
            panic("err")
        }
        defer file.Close()

        // 讀出文件
        b, err := ioutil.ReadAll(file)
        if err != nil {
            panic("err")
        }

        // 寫入文件到頁面
        writer.Write(b)
    })

    // 監(jiān)聽端口:8888
    err := http.ListenAndServe(":8888", nil)
    if  err != nil {
        panic("err")
    }
}

這里面主要注意一下我們對錯誤的處理. 都是直接打出panic. 這樣是很不友好的.

如果頁面輸入的文件路徑不對, 則直接404

按照之前第二步說的, 我們應該對panic進行處理. 比如打開文件的操作, 我們改為如下

// 打開文件
file, err := os.Open(path)
if err != nil {
    http.Error(writer, err.Error(), http.StatusInternalServerError)  return
}
defer file.Close()

這樣就好多了, 起碼程序不會直接拋出異常

這是將系統(tǒng)的錯誤直接打出了, 比上面好一些, 但也不是特別友好, 通常我們不希望吧系統(tǒng)內(nèi)部錯誤輸出出來. 我們希望經(jīng)過包裝后輸出錯誤

于是做了如下修改.

第一步: 將http.handleFunc中的函數(shù)部分提出來, 這部分是業(yè)務邏輯.

提出來以后做了如下修改. 1. 函數(shù)增加一個返回值error. 2. 遇到錯誤,直接return. 如下紅色標出部分

package fileListener

import (
    "io/ioutil"
    "net/http"
    "os"
)

func FileHandler(writer http.ResponseWriter, request *http.Request) error{
    // 獲取url路徑, 路徑是/list/之后的部分
    path := request.URL.Path[len("/list/"):]
    // 打開文件
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()

    // 讀出文件
    b, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }

    // 寫入文件到頁面
    writer.Write(b)
    return nil
}

第二: 封裝錯誤內(nèi)容

這里就體現(xiàn)了函數(shù)式編程的特點, 靈活

// 定義一個函數(shù)類型的結(jié)構(gòu), 返回值是erro 
type Handler func(writer http.ResponseWriter, request *http.Request) error

// 封裝error
func WrapHandler(handler Handler) func (http.ResponseWriter, *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        // 執(zhí)行原來的邏輯. 然后增加error的錯誤處理
        err := handler(writer, request)
        if err != nil {
            code := http.StatusOK
            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound

            case os.IsPermission(err):
                code = http.StatusServiceUnavailable
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer, http.StatusText(code), code)
        }
    }
}

調(diào)用的部分

// 我們來模擬一個web服務器. 在url上輸入一個地址, 然后顯示文件內(nèi)容
// 做一個顯示文件的web server
func main() {
    http.HandleFunc("/list/", WrapHandler(fileListener.FileHandler))

    // 監(jiān)聽端口:8888
    err := http.ListenAndServe(":8888", nil)
    if  err != nil {
        panic("err")
    }
}

這樣, 當我們再次輸入錯誤的文件路徑時, 提示信息如下:

四、panic

發(fā)生panic的時候, 會做那些事呢?

1. 停止當前函數(shù)的執(zhí)行

2. 一直向上返回, 執(zhí)行每一層的defer

3. 如果沒有遇到recover, 程序就退出

五、recover

1. 在defer 中調(diào)用

2. 獲取panic的值

3. 如果無法處理, 可以重新panic

package main

import (
    "fmt"
    "github.com/pkg/errors"
)

func tryRecover() {

    defer func(){
        r := recover()
        if r, ok := r.(error); ok {
            fmt.Println("error 發(fā)生", r.Error())
        } else {
            panic(fmt.Sprintf("未知錯誤:%v", r))
        }
    }()
    panic(errors.New("錯誤"))

}

func main() {
    tryRecover()
}

六、error vs panic

七、錯誤處理綜合示例

第五條的案例, 我們進行了error的統(tǒng)一管理, 但是還沒有對其他異常進行recover, 還有可能導致程序崩潰. 比如http://localhost:8888/abc. 繼續(xù)優(yōu)化代碼.

這樣很不友好, 我們在看看控制臺, 發(fā)現(xiàn)程序并沒有掛掉, 這是為什么呢? 想象一下, 應該是程序自動給我們recover了.

我們來看看server.go

原來server.go已經(jīng)幫我們recover了, recover后并不是中斷進程, 而是打印輸出錯誤日志. 雖然如此, 但頁面顯示依然很難看. 因此我們要做兩件事

1. 如果出現(xiàn)異常, 我們自己進行recover, 那么他就不會走系統(tǒng)定義的recover了. 這還不夠, 這只是說控制臺不會再打印出一大堆藍色異常代碼了. 我們還有做第二件事

2. 將出現(xiàn)異常的位置捕獲出來, 并且, 打印到頁面

第一步: 自定一定recover, 代替server.go中的recover

// 封裝error
func WrapError(handler Handler) func (http.ResponseWriter, *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        defer func(){
            if r := recover(); r != nil {
                fmt.Println("發(fā)生錯誤")
                http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
            }
        }()

        // 執(zhí)行原來的邏輯. 然后增加error的錯誤處理
        err := handler(writer, request)
        if err != nil {
            code := http.StatusOK
            switch {
            case os.IsNotExist(err):
                code = http.StatusNotFound

            case os.IsPermission(err):
                code = http.StatusServiceUnavailable
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer, http.StatusText(code), code)
        }
    }
}

這樣異常就被我們捕獲了, 頁面打印出

這樣就好看多了. 我們在對代碼進行優(yōu)化

我們將發(fā)生異常的地方進行處理

func FileHandler(writer http.ResponseWriter, request *http.Request) error {
    // 獲取url路徑, 路徑是/list/之后的部分
    if c := strings.Index(request.URL.Path, "/list/"); c != 0 {
        return errors.New("url 不是已list開頭")
    }
    path := request.URL.Path[len("/list/"):]
    // 打開文件
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()

    // 讀出文件
    b, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }

    // 寫入文件到頁面
    writer.Write(b)
    return nil
}

頁面打印效果

我們發(fā)現(xiàn)這個打印的還是系統(tǒng)給出的錯誤異常. 那么,我們有沒有辦法, 把這個異常打印出來呢?

if c := strings.Index(request.URL.Path, "/list/"); c != 0 {
    return errors.New("url 不是已list開頭")
}

我們自己來定義一個異常處理的接口

type userError interface {
    error        // 系統(tǒng)異常
    Message() string    // 用戶自定義異常
}

接口定義好了, 在哪里用呢? 你想打印出自己的異常信息, 那就不能打印系統(tǒng)的. 自定義信息在系統(tǒng)異常之前判斷

// 執(zhí)行原來的邏輯. 然后增加error的錯誤處理
err := handler(writer, request)
if err != nil {
    if userErr, ok := err.(userError); ok {
        http.Error(writer, userErr.Message(), http.StatusBadRequest)
        return
    }
    code := http.StatusOK
    switch {
    case os.IsNotExist(err):
        code = http.StatusNotFound

    case os.IsPermission(err):
        code = http.StatusServiceUnavailable
    default:
        code = http.StatusInternalServerError
    }
    http.Error(writer, http.StatusText(code), code)
}

接下來是具體實現(xiàn)了, 現(xiàn)在用戶想要實現(xiàn)自定義一個userError. 然后設(shè)置異常類型為userError

type userError string

func (u userError) Error() string{
    return u.Message()
}

func (u userError) Message() string {
    return string(u)
}
func FileHandler(writer http.ResponseWriter, request *http.Request) error {
    // 獲取url路徑, 路徑是/list/之后的部分
    if c := strings.Index(request.URL.Path, "/list/"); c != 0 {
        return userError("url 不是已list開頭")
    }
    path := request.URL.Path[len("/list/"):]
    // 打開文件
    file, err := os.Open(path)
    if err != nil {
        return err
    }
    defer file.Close()

    // 讀出文件
    b, err := ioutil.ReadAll(file)
    if err != nil {
        return err
    }

    // 寫入文件到頁面
    writer.Write(b)
    return nil
}

這樣一個實現(xiàn)自定義打印異常的功能就做好了. 異常也是可以封裝的.

最后再來梳理這個小案例:

1. 我們有一個想法, 模擬web請求, 在瀏覽器url上輸入一個文件路徑, 打印文件的內(nèi)容

2. 內(nèi)容可能有錯誤, 進行異常處理.

3. 有時候異常拋出的是系統(tǒng)給出, 我們自己對異常進行recover, 然后打印出來

4. 打印自定義異常.

以下是完整代碼

package handling

import (
    "io/ioutil"
    "net/http"
    "os"
    "strings"
)

type UserError struct {
    Content string
}

func (u UserError) Error() string {
    return u.Message()
}

func (u UserError) Message() string {
    return u.Content
}

func Hanldering(writer http.ResponseWriter, request *http.Request) error {
    // 獲取url, list之后的就是url
    if s := strings.Index(request.URL.Path, "/list/"); s != 0 {
        return UserError{"path error, /list/"}
    }
    url := request.URL.Path[len("/list/"):]

    // 根據(jù)url打開文件
    file, err := os.Open(url)
    if err != nil {
        return os.ErrNotExist
    }
    defer file.Close()

    // 打開以后把文件內(nèi)容讀出來
    f, err := ioutil.ReadAll(file)
    if err != nil {
        return os.ErrPermission
    }

    // 讀出來以后, 寫入到頁面
    writer.Write(f)
    return nil
}
package main

import (
    "aaa/handlerError/linstenerFile/handling"
    "github.com/siddontang/go/log"
    "net/http"
    "os"
)

type ErrorHandlering func(writer http.ResponseWriter, request *http.Request) error

func WrapError(handler ErrorHandlering) func(http.ResponseWriter, *http.Request) {
    return func(writer http.ResponseWriter, request *http.Request) {
        defer func() {
            if r := recover(); r != nil {
                log.Warn("other error")
                http.Error(writer, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
            }
        }()

        err := handler(writer, request)

        //自定義異常處理

        // 錯誤處理
        if err != nil {
            if userErr, ok := err.(UserError); ok {
                log.Warn("user error:", userErr.Message())
                http.Error(writer, userErr.Message(), http.StatusBadRequest)
                return
            }
            code := http.StatusOK
            switch err {
            case os.ErrNotExist:
                code = http.StatusNotFound
            case os.ErrPermission:
                code = http.StatusBadRequest
            default:
                code = http.StatusInternalServerError
            }
            http.Error(writer, http.StatusText(code), code)

        }
    }
}

type UserError interface {
    error
    Message() string
}




func main() {
    // 模擬web請求
    http.HandleFunc("/", WrapError(handling.Hanldering))

    // 指定服務端口
    http.ListenAndServe(":8888", nil)
}

以上就是詳解Go語言的錯誤處理和資源管理的詳細內(nèi)容,更多關(guān)于Go 錯誤處理 資源管理的資料請關(guān)注腳本之家其它相關(guān)文章!

您可能感興趣的文章:
  • 詳解Go多協(xié)程并發(fā)環(huán)境下的錯誤處理
  • 關(guān)于Mongodb參數(shù)說明與常見錯誤處理的總結(jié)
  • Golang巧用defer進行錯誤處理的方法
  • Go語言中更優(yōu)雅的錯誤處理
  • GO語言標準錯誤處理機制error用法實例
  • Django靜態(tài)資源部署404問題解決方案
  • Django跨域資源共享問題(推薦)
  • 基于Django靜態(tài)資源部署404的解決方法
  • Django靜態(tài)資源URL STATIC_ROOT的配置方法

標簽:蘭州 欽州 雞西 吐魯番 重慶 汕頭 銅川 梅河口

巨人網(wǎng)絡通訊聲明:本文標題《詳解Go語言的錯誤處理和資源管理》,本文關(guān)鍵詞  詳解,語言,的,錯誤,處理,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《詳解Go語言的錯誤處理和資源管理》相關(guān)的同類信息!
  • 本頁收集關(guān)于詳解Go語言的錯誤處理和資源管理的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章