7、go语言:错误处理和资源管理

1、defer调用:

go语言通过defer调用来实现资源管理,defer先进后出的顺序

确保调用在函数结束时发生

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

//输出
3
2
1
panic: error occurred

func writeFile(filename string){
	file ,err := os.Create(filename)
	if err != nil {
		panic("err")
	}
	defer file.Close()
	
	writer := bufio.NewWriter(file) //用bufio写入到内存,待内存到一定程度的时候再写入文件,会快很多
	defer writer.Flush() //需要刷新,不然数据还在缓存中不会写入到文件
	f := fib.Fibonacci()
	for i := 0;i<20;i++{
		fmt.Fprintln(writer,f())
	}
}

确保调用在函数结束时发生
参数在defer语句时计算
defer列表为后进先出

何时使用defer?
Open/Close
Lock/Unlock
PrintHeader/PrintFooter

2、错误概念处理:

func writeFile(filename string){
	file ,err := os.OpenFile(filename,os.O_EXCL|os.CREATE,0666)
	if err != nil {
		//panic("err")
		//正确的处理方式
		//fmt.Println("file already exists")
		//return
		
		//或者
		if pathError,ok := err.(*os.PathError);!ok{
			panic(err)
		}else{
			fmt,Println(pathError.Op,
			pathError.Path,
			pathError.Err)
		}
		return
	}
	defer file.Close()
	
	writer := bufio.NewWriter(file) //用bufio写入到内存,待内存到一定程度的时候再写入文件,会快很多
	defer writer.Flush() //需要刷新,不然数据还在缓存中不会写入到文件
	f := fib.Fibonacci()
	for i := 0;i<20;i++{
		fmt.Fprintln(writer,f())
	}
}

3、服务器统一错误处理:

如何实现统一的错误处理逻辑?

func main(){
	http.HandleFunc("/list/",
		func(writer http.ResponseWriter,
			request *http.Request){
				path := request.URL.Path[len("/list/"):]
				file,err :=os.Open(path)
				if err != nil {
					//panic(err)
					http.Error(writer,
					err.Error(),
					http.StatusInternalServerError)
					return
				}
				defer file.Close()
				all,err := ioutil.ReadAll(file)
				if err != nil {
					panic(err)
				}
				writer.Writer(all)
				})
				
		err := http.ListenAndServe(":8888",nil)
		if err != nil {
			panic(err)
		}
}

=======================================
//提取函数:

func HandleFileLis(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)	
			return err
		}
		writer.Writer(all)
		return nil
}

type appHandler func(writer http.ResponseWriter,
	request *http.Request) error //定义一个函数

//函数式编程,输入函数,输出函数
func errWrapper(hander 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.IsNotExists(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)
	}
}

4、panic和recover:

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

recover:
仅在defer调用中使用
获取panic的值
如果无法处理,可重新panic

func tryRecover(){
	defer func(){
	r := recover()
	if err,ok := r.(error);ok { //断言
		fmt.Println("Error occurred:",err)
	}else{
		panic(r)
	}
	}() //加括号调用
	//panic(errors.New("this is an error"))
	
	b := 0
	a := 5/b
	fmt.Println(a) //如果没有recover,程序就挂掉了
}

5、服务器统一错误处理2:

//函数式编程,输入函数,输出函数
func errWrapper(hander appHandler) func(http.ResponseWriter,*http.Request){
	return func(writer http.ResponseWriter,
		request *http.Request){
		
			//这里加入recover
			defer func(){
				r := recover()
				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())
				code = http.StatusOK
				switch{
					case os.IsNotExists(err):
						code = http.StatusNotFound
					case os.IsPermission(err):
						code = http.StatusForbidden
					default:
						code = http.StatusInternalServerError
			}
			http.Error(
				writer,
				http.StatusText(code),
				code)
			}
		}
}

--------------------------------------------------------
const prefix = "/list/"
func HandleFileLis(writer http.ResponseWriter,
request *http.Request) error{
	if strings.Index(request.URL.Path,prefix) != 0 {
		return errors.New("path must start " + "with " + 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)	
		return err
	}
	writer.Writer(all)
	return nil
}

//有些错误是可以给用户看到,有些不能给用户看到

type userError interface{
	error
	Message() string
}

type userError string

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

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

//函数式编程,输入函数,输出函数
func errWrapper(hander appHandler) func(http.ResponseWriter,*http.Request){
	return func(writer http.ResponseWriter,
		request *http.Request){
		
			//这里加入recover
			defer func(){
				r := recover()
				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())
				//------------------区分给用户看到的错误类型-------------------------
				if userErr,ok := err.(userError);ok{
					http.Error(writer,
						userErr.Message(),
						http.StatusBadRequest)
				return
			}
			//----------------------------------------------------------------------
			code = http.StatusOK
			switch{
				case os.IsNotExists(err):
					code = http.StatusNotFound
				case os.IsPermission(err):
					code = http.StatusForbidden
				default:
					code = http.StatusInternalServerError
			}
			http.Error(
				writer,
				http.StatusText(code),
				code)
			}
		}
}

func HandleFileLis(writer http.ResponseWriter,
request *http.Request) error{
	if strings.Index(request.URL.Path,prefix) != 0 {
		return userError("path must start " + "with " + 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)	
		return err
	}
	writer.Writer(all)
	return nil
}

error vs panic
意料之中的:使用error。如:文件打不开
意料之外的:使用panic。如:数组越界
defer + panic + recover
Type Assertion
函数式编程的应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值