一.defer调用:实现资源管理
- 确保调用在函数结束时发生
- 参数在defer语句时计算
- defer列表为后进先出
何时使用defer调用
- Open/Close
- Lock/Unlock
- PrintHeader/PrintFooter
package main import "fmt" func tryDefer(){ defer fmt.Println(1) defer fmt.Println(2)//defer 相当于栈:先进后出 fmt.Println(3) //结果:3 2 1 } func main() { tryDefer() }
package main import ( "../../functional/fib" "bufio" "fmt" "os" ) func tryDefer() { defer fmt.Println(1) defer fmt.Println(2) //defer 相当于栈:先进后出 fmt.Println(3) //结果:3 2 1 //return panic("error occurred") fmt.Println(4) //3 //2 //1 //panic: error occurred } //参数在defer语句时计算 func tryDefer2() { for i := 0; i < 100; i++ { defer fmt.Println(i) // if i == 10 { panic("print too many") } } } func writeFile(filename string) { file, err := os.Create(filename) if err != nil { panic(err) } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() f := fib.Fibonacci() for i := 0; i < 10; i++ { fmt.Fprintln(writer, f()) } } func main() { //tryDefer() tryDefer2() writeFile("fib.txt") }
二.错误处理理念
func writeFile(filename string) { //file, err := os.Create(filename) file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) //O_EXCL 如果文件存在的话打开不了 // panic: open fib.txt: file exists err = errors.New("this is a custum error") if err != nil { //fmt.Println("Error:",err.Error()) if pathError, ok := err.(*os.PathError); !ok { panic(err) } else { fmt.Printf("%s ,%s ,%s\n ", pathError.Op, pathError.Path, pathError.Err) } return } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() f := fib.Fibonacci() for i := 0; i < 10; i++ { fmt.Fprintln(writer, f()) } }
三.服务器统一出错处理
web.go
package main import ( "./filelisting" "log" "net/http" "os" ) type appHandler func(writer http.ResponseWriter, request *http.Request) error //错误包装 func errWrapper(handler appHandler) func(http.ResponseWriter, *http.Request) { return func(writer http.ResponseWriter, request *http.Request) { err := handler(writer, request) if err != nil { log.Printf("Error handing request:%s",err.Error()) //2020/08/29 17:17:36 Error handing request:open fib2.txt: permission denied code := http.StatusOK switch { case os.IsNotExist(err): //http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound) code = http.StatusNotFound case os.IsPermission(err): code = http.StatusForbidden default: code = http.StatusInternalServerError } http.Error(writer, http.StatusText(code), code) } } } func main() { http.HandleFunc("/list/", errWrapper(filelisting.HandleFileList)) err := http.ListenAndServe(":8081", nil) if err != nil { panic(err) } }
handler.go
package filelisting import ( "io/ioutil" "net/http" "os" ) func HandleFileList(writer http.ResponseWriter, request *http.Request) error { path := request.URL.Path[len("/list/"):] file, err := os.Open(path) if err != nil { //panic(err) /*http.Error(writer, err.Error(), http.StatusInternalServerError) return*/ return err } defer file.Close() all, err := ioutil.ReadAll(file) if err != nil { //panic(err) return err } writer.Write(all) return nil }
四.panic和recover
panin功能作用
- 停止当前函数执行
- 一直向上返回,执行每一层的defer
- 如果没有遇见recover,程序退出
recover功能作用
- 仅在defer调用中使用
- 获取panic的值
- 如果无法处理,可重新panic
package main import ( "fmt" ) func tryRecover() { defer func() { r := recover() if err, ok := r.(error); ok { fmt.Println("Error occurred:", err) } else { fmt.Println(r) } }() //panic(errors.New("this is an error")) //Error occurred: this is an error,下面不执行,panic停止当前函数执行 b := 0 a := 5 / b fmt.Println(a) //Error occurred: runtime error: integer divide by zero } func main() { tryRecover() }
五.服务器统一出错处理
- 意料之中的:使用error,如文件打不开
- 意料之外的:使用panic,如数组越界