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

詳解Go語(yǔ)言的錯(cuò)誤處理和資源管理

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

一、defer

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

2. defer列表為先進(jìn)后出

3. 參數(shù)在defer語(yǔ)句時(shí)計(jì)算.

下面來(lái)看一個(gè)例子: 寫入文件

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ù)列寫入文件. 這里有兩個(gè)資源使用. 1. 創(chuàng)建文件, 然后文件關(guān)閉. 2. 寫入資源, 將資源從緩存中刷入文件. 這兩個(gè)操作都應(yīng)該應(yīng)該是成對(duì)出現(xiàn)的, 因此, 用defer 語(yǔ)句, 避免后面寫著寫著忘了, 也保證即使出錯(cuò)了, 也能夠執(zhí)行defer語(yǔ)句的內(nèi)容

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

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

打印結(jié)果:

9

8

7

6

5

4

3

2

1

0

二、錯(cuò)誤處理

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

比如下面的操作:

package main

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

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

    // 這里就對(duì)錯(cuò)誤的類型進(jìn)行了捕獲處理.
    if err, ok := err.(*os.PathError); !ok {
        fmt.Println("未知錯(cuò)誤")
    } 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")
}

紅色字體部分就是對(duì)錯(cuò)誤進(jìn)行了捕獲處理.

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

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

package main

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

// 我們來(lái)模擬一個(gè)web服務(wù)器. 在url上輸入一個(gè)地址, 然后顯示文件內(nèi)容
// 做一個(gè)顯示文件的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")
        }

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

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

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

如果頁(yè)面輸入的文件路徑不對(duì), 則直接404

按照之前第二步說的, 我們應(yīng)該對(duì)panic進(jìn)行處理. 比如打開文件的操作, 我們改為如下

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

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

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

于是做了如下修改.

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

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

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
    }

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

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

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

// 定義一個(gè)函數(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í)行原來(lái)的邏輯. 然后增加error的錯(cuò)誤處理
        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)用的部分

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

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

這樣, 當(dāng)我們?cè)俅屋斎脲e(cuò)誤的文件路徑時(shí), 提示信息如下:

四、panic

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

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

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

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

五、recover

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

2. 獲取panic的值

3. 如果無(wú)法處理, 可以重新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("未知錯(cuò)誤:%v", r))
        }
    }()
    panic(errors.New("錯(cuò)誤"))

}

func main() {
    tryRecover()
}

六、error vs panic

七、錯(cuò)誤處理綜合示例

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

這樣很不友好, 我們?cè)诳纯纯刂婆_(tái), 發(fā)現(xiàn)程序并沒有掛掉, 這是為什么呢? 想象一下, 應(yīng)該是程序自動(dòng)給我們r(jià)ecover了.

我們來(lái)看看server.go

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

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

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

第一步: 自定一定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ā)生錯(cuò)誤")
                http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
            }
        }()

        // 執(zhí)行原來(lái)的邏輯. 然后增加error的錯(cuò)誤處理
        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è)面打印出

這樣就好看多了. 我們?cè)趯?duì)代碼進(jìn)行優(yōu)化

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

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
    }

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

頁(yè)面打印效果

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

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

我們自己來(lái)定義一個(gè)異常處理的接口

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

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

// 執(zhí)行原來(lái)的邏輯. 然后增加error的錯(cuò)誤處理
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)
}

接下來(lái)是具體實(shí)現(xiàn)了, 現(xiàn)在用戶想要實(shí)現(xiàn)自定義一個(gè)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
    }

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

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

最后再來(lái)梳理這個(gè)小案例:

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

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

3. 有時(shí)候異常拋出的是系統(tǒng)給出, 我們自己對(duì)異常進(jìn)行recover, 然后打印出來(lái)

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)容讀出來(lái)
    f, err := ioutil.ReadAll(file)
    if err != nil {
        return os.ErrPermission
    }

    // 讀出來(lái)以后, 寫入到頁(yè)面
    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)

        //自定義異常處理

        // 錯(cuò)誤處理
        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請(qǐng)求
    http.HandleFunc("/", WrapError(handling.Hanldering))

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

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

相關(guān)文章

  • graphql---go http請(qǐng)求使用詳解

    graphql---go http請(qǐng)求使用詳解

    這篇文章主要介紹了graphql---go http請(qǐng)求使用詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧
    2020-12-12
  • 一文帶你了解Go語(yǔ)言fmt標(biāo)準(zhǔn)庫(kù)輸入函數(shù)的使用

    一文帶你了解Go語(yǔ)言fmt標(biāo)準(zhǔn)庫(kù)輸入函數(shù)的使用

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

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

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

    GoFrame錯(cuò)誤處理常用方法及錯(cuò)誤碼使用示例

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

    Go語(yǔ)言hello world實(shí)例

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

    Golang中switch語(yǔ)句和select語(yǔ)句的用法教程

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

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

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

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

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

    golang?四則運(yùn)算計(jì)算器yacc歸約手寫實(shí)現(xiàn)

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

    使用Go語(yǔ)言編寫一個(gè)極簡(jiǎn)版的容器Container

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

最新評(píng)論