Go资源管理与出错处理

defer 调用

  • 确保调用在函数结束时发生,即使中间发生panic,defer也会被执行;defer的调用顺序是先进后出
package main

import "fmt"

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

输出结果:

3
2
1
panic: error occurred

goroutine 1 [running]:
main.tryDefer()
	C:/Users/ehuamay/go/src/awesomeProject/8-1-defer/defer/defer.go:9 +0x163
main.main()
	C:/Users/ehuamay/go/src/awesomeProject/8-1-defer/defer/defer.go:13 +0x27

Process finished with exit code 2

  • 参数在defer语句时计算
  • defer列表为后进先出

何时使用defer调用

  • Open/Close
  • Lock/Unlock
  • PrintHeader/PrintFooter
package main

import (
	"fmt"
	"os"
	"bufio"
	"awesomeProject/8-1-defer/fibo"
	)



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

	//err = errors.New("This is a customer error")
	if err != nil {
		//panic(err)
		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()

	write := bufio.NewWriter(file)
	defer write.Flush()

	f := fibo.Fibonacci()
	for i := 0; i < 20; i++ {
		fmt.Fprintln(write, f())
	}
}
func main() {
	writeFile("fib.txt")
}

如何统一处理err

可以用函数式编程的思想统一处理error

package filelisting

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

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 err
	}
	defer file.Close()

	all, err := ioutil.ReadAll(file)
	if err != nil {
		panic(err)
	}

	writer.Write(all)
	return nil
}
package main

import (
	"net/http"
	"awesomeProject/8-3/filelistingserver/filelisting"
	"os"
	"log"
)

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())
			log.Printf("Error occured" +
				" 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(":8000", nil)
	if err != nil {
		panic(err)
	}
}

panic

  • 停止当前函数执行
  • 一直向上返回,执行每一层的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 {
			panic(fmt.Sprintf("I don't known what to do: %v", r))
		}
	}()
	//panic(errors.New("This is an error"))
	/*b := 0
	a := 5 / b
	fmt.Println(a)*/
	panic(123)
}
func main() {
	tryRecover()
}

error VS panic

  • 意料之中的:使用error。如:文件打不开
  • 意料之外:使用panic。如:数组越界
package filelisting

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

const prefix  = "/list/"
type userError string

func (e userError) Error() string {
	return e.Message()
}

func (e userError) Message() string {
	return string(e)
}

func HandleFileList (writer http.ResponseWriter, request *http.Request) error{
	if strings.Index(request.URL.Path, prefix) != 0 {
		return userError(fmt.Sprintf("path %s must start " +
			"with %s", request.URL.Path, prefix))
	}
	path := request.URL.Path[len(prefix):]
	file, err := os.Open(path)
	if err != nil {
		/*//panic(err)
		http.Error(writer,
			err.Error(),
			http.StatusInternalServerError)*/
		return err
	}
	defer file.Close()

	all, err := ioutil.ReadAll(file)
	if err != nil {
		panic(err)
	}

	writer.Write(all)
	return nil
}
package main

import (
	"net/http"
	"os"
	"log"
	"awesomeProject/8-5-unifyerror/filelistingserver/filelisting"
)

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) {
		defer func() {
			if r := recover(); r != nil{
				log.Printf("panic: %v", r)
				http.Error(writer,
					http.StatusText(http.StatusInternalServerError),
					http.StatusInternalServerError)
			}
		}()
		err := handler(writer, request)
		if err != nil {
			//log.Warn("Error handling request:%s", err.Error())
			log.Printf("Error occured" +
				" handling request:%s",
				err.Error())

			//user error
			//userErr, _ := err.(userError)
			log.Printf("%v", err)
			if userErr, ok := err.(userError); ok {
				http.Error(writer,
					userErr.Message(),
					http.StatusBadRequest)
				return
			}

			//system 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)
		}
	}
}

type userError interface {
	error
	Message() string
}
func main() {
	http.HandleFunc("/", errWrapper(filelisting.HandleFileList))
	err := http.ListenAndServe(":8000", nil)
	if err != nil {
		panic(err)
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值