• Google资深工程师深度讲解Go语言错误处理和资源管理(七)


    一.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,如数组越界

    赞赏码

    非学,无以致疑;非问,无以广识

  • 相关阅读:
    使用Feign访问接口
    IDEA 更改提示一键补全快捷键
    Mac配置Gradle环境
    RestSharp Simple REST and HTTP API Client for .NET
    Vue 一个注册页面有省市联动
    Authentication 接口验证访问 (C#)
    WebService快速入门文档
    自己写了个MongoDB的CRUD文档
    RabbitMQ入门教程
    dubbo简单入门使用
  • 原文地址:https://www.cnblogs.com/lxwphp/p/15452739.html
Copyright © 2020-2023  润新知