GO 错误处理与资源管理

0 前言

合理,成对使用

1、资源管理与出错处理

1.1 defer调用

  • go语言是通过defer调用来实现资源管理的
  • 确保调用在函数结束时候发生

简单例子1

package main

import "fmt"

func tryDefer(){
	defer fmt.Println(1)
	defer fmt.Println(2)
	fmt.Println(3)
	//return
	panic("error occurred")
	fmt.Println(4)
}

func main(){
	tryDefer()
}
//return 
3
2
1
panic: error occurred

goroutine 1 [running]:
main.tryDefer()
        /Users/*******/GolandProjects/gotest/src/defer/defer.go:10 +0x165
main.main()
        /Users/*******/GolandProjects/gotest/src/defer/defer.go:15 +0x25

Process finished with exit code 2

defer的运行顺序是这个样子的:

  1. 不运行tryDefer函数中的defer语句,运行完毕函数
  2. 然后按照栈的性质一次运行defer语句
  3. 中间有return或者panic都不会影响defer语句的运行

简单例子2

package main

import (
	"bufio"
	"fmt"
	"os"
)

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 := fibonacci()
	for i := 0; i < 20; i++{
		fmt.Fprintln(writer, f())
	}
}

func main(){
	writeFile("src/abc.txt")
}

2、出错处理概念

func writeFile(filename string){
	file, err := os.OpenFile(
		filename, os.O_EXCL|os.O_CREATE, 0666)

	if err != nil{
		if pathError, ok := err.(*os.PathError); !ok {
			panic(err)
		}else{
			fmt.Println(pathError.Op,
				pathError.Path,
				pathError.Err)
		}
		return
	}

	if err != nil{
		panic(err)
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	defer writer.Flush()
	f := fibonacci()
	for i := 0; i < 20; i++{
		fmt.Fprintln(writer, f())
	}
}

2.1 自定义error

func writeFile(filename string){
	file, err := os.OpenFile(
		filename, os.O_EXCL|os.O_CREATE, 0666)
	err = errors.New("this is a custom error")
	if err != nil{
		if pathError, ok := err.(*os.PathError); !ok {
			panic(err)
		}else{
			fmt.Println(pathError.Op,
				pathError.Path,
				pathError.Err)
		}
		return
	}

	if err != nil{
		panic(err)
	}
	defer file.Close()

	writer := bufio.NewWriter(file)
	defer writer.Flush()
	f := fibonacci()
	for i := 0; i < 20; i++{
		fmt.Fprintln(writer, f())
	}
}

3、服务器统一出错处理

3.1 实现统一的错误处理逻辑

将错误的代码放到一个函数中

// main.go

package main

import (
	"filelistingserver/filelisting"
	"github.com/gpmgo/gopm/modules/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.Warn("Error handling request : %s", err.Error())
			code := http.StatusOK
			switch {
			case os.IsNotExist(err):
				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(":8888", nil)
	if err != nil{
		panic(err)
	}
}
// filelist.go

package filelisting

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

func HandlefileList(writer http.ResponseWriter, request *http.Request) error{
	path := request.URL.Path[len("/list/"): ] // /list/fib.txt
	file, err := os.Open(path)
	if err != nil{
		return err
	}
	defer file.Close()
	all, err := ioutil.ReadAll(file)
	if err != nil{
		return err
	}
	writer.Write(all)
	return nil
}

4、panic和recover

4.1 panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的defer
  • 如果没有遇见recover,程序退出

4.2 recover

  • 仅在defer中调用
  • 获取panic值
  • 如果无法获取处理,可重新panic
package main

import (
	"fmt"
)

func tryRecover(){
	defer func() {
		r := recover()
		if err, ok := r.(error); ok{
			fmt.Println("Error occured: ", err)
		}else{
			panic(r)
		}
	}()
	// panic(errors.New("this is an error"))
	//b := 0
	//a := 5 / b
	//fmt.Println(a)

	// 此时如果报一个123的错误,recover不知道这是啥,所以就没有办法去处理,所以会panic出来
	panic(123)
}

func main() {
	tryRecover()
}
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页