欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2021年06月23日 14:26:39   作者:盛開的太陽  
資源處理是什么?打開文件需要關(guān)閉,打開數(shù)據(jù)庫連接,連接需要釋放。這些成對出現(xiàn)的就是資源管理。有時候我們雖然釋放了,但是程序在中間出錯了,那么可能導(dǎo)致資源釋放失敗。如何保證打開的文件一定會被關(guān)閉呢?這就是資源管理與錯誤處理考慮的一個原因

一、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. 寫入資源, 將資源從緩存中刷入文件. 這兩個操作都應(yīng)該應(yīng)該是成對出現(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這樣導(dǎo)致系統(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服務(wù)器, 在瀏覽器地址欄輸入文件的url, 然后顯示文件的內(nèi)容. 比如斐波那契數(shù)列的文件

package main

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

// 我們來模擬一個web服務(wù)器. 在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

按照之前第二步說的, 我們應(yīng)該對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è)務(wù)邏輯.

提出來以后做了如下修改. 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服務(wù)器. 在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, 還有可能導(dǎo)致程序崩潰. 比如http://localhost:8888/abc. 繼續(xù)優(yōu)化代碼.

這樣很不友好, 我們在看看控制臺, 發(fā)現(xiàn)程序并沒有掛掉, 這是為什么呢? 想象一下, 應(yīng)該是程序自動給我們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))

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

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

相關(guān)文章

  • graphql---go http請求使用詳解

    graphql---go http請求使用詳解

    這篇文章主要介紹了graphql---go http請求使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 一文帶你了解Go語言fmt標準庫輸入函數(shù)的使用

    一文帶你了解Go語言fmt標準庫輸入函數(shù)的使用

    這篇文章主要為大家詳細介紹了Go語言中?fmt?標準庫輸入函數(shù)的使用,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-01-01
  • Go調(diào)度器學(xué)習(xí)之系統(tǒng)調(diào)用詳解

    Go調(diào)度器學(xué)習(xí)之系統(tǒng)調(diào)用詳解

    這篇文章腫,將以一個簡單的文件打開的系統(tǒng)調(diào)用,來分析一下Go調(diào)度器在系統(tǒng)調(diào)用時做了什么。文中的示例代碼講解詳細,需要的可以參考一下
    2023-04-04
  • GoFrame錯誤處理常用方法及錯誤碼使用示例

    GoFrame錯誤處理常用方法及錯誤碼使用示例

    這篇文章主要為大家介紹了GoFrame錯誤處理常用方法及錯誤碼使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06
  • Go語言hello world實例

    Go語言hello world實例

    這篇文章主要介紹了Go語言hello world實例,本文先是給出了hello world的代碼實例,然后對一些知識點和技巧做了解釋,需要的朋友可以參考下
    2014-10-10
  • Golang中switch語句和select語句的用法教程

    Golang中switch語句和select語句的用法教程

    這篇文章主要給大家介紹了關(guān)于Golang中switch和select的用法教程,文中通過示例代碼將switch語句與select語句的使用方法介紹的非常詳細,對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編一起來學(xué)習(xí)學(xué)習(xí)吧。
    2017-06-06
  • Go語言之重要數(shù)組類型切片(slice)make,append函數(shù)解讀

    Go語言之重要數(shù)組類型切片(slice)make,append函數(shù)解讀

    這篇文章主要介紹了Go語言之重要數(shù)組類型切片(slice)make,append函數(shù)用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • go語言代碼生成器code?generator使用示例介紹

    go語言代碼生成器code?generator使用示例介紹

    這篇文章主要為大家介紹了go語言代碼生成器code?generator的使用簡單介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-05-05
  • golang?四則運算計算器yacc歸約手寫實現(xiàn)

    golang?四則運算計算器yacc歸約手寫實現(xiàn)

    這篇文章主要為大家介紹了golang?四則運算?計算器?yacc?歸約的手寫實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • 使用Go語言編寫一個極簡版的容器Container

    使用Go語言編寫一個極簡版的容器Container

    Docker作為一種流行的容器化技術(shù),對于每一個程序開發(fā)者而言都具有重要性和必要性,因為容器化相關(guān)技術(shù)的普及大大簡化了開發(fā)環(huán)境配置、更好的隔離性和更高的安全性,對于部署項目和團隊協(xié)作而言也更加方便,本文將嘗試使用Go語言編寫一個極簡版的容器
    2023-10-10

最新評論