```
feat(error): 添加Go语言异常处理完整教程 详细介绍了Go语言中的错误处理机制,包括: - error类型的处理方式,通过返回值处理异常 - panic和recover的使用方法,以及如何从程序崩溃中恢复 - defer函数的应用,用于资源清理和崩溃恢复 - 错误创建的不同方式:errors.New、fmt.Errorf、errors.Join - 错误比较的最佳实践:使用errors.Is进行错误判断 - 实际代码示例展示了目录遍历中的错误处理 - 添加了协程调度的架构图解 ```
This commit is contained in:
parent
2e534c1fac
commit
bb8564ba49
@ -1 +1,159 @@
|
||||
# 异常处理
|
||||
# 异常处理
|
||||
|
||||
程序运行过程中,可能会有异常,比如: 输入参数错误,文件不存在,数据库连接失败等等
|
||||
|
||||
## error (程序异常)
|
||||
|
||||
### 处理 error
|
||||
|
||||
异常处理的流派:
|
||||
+ 通过返回值值处理: resp, error := func() (resp, error)
|
||||
+ try catch: try { ... } catch(e) { ... }, 面向对象的语言: java, python ,js,
|
||||
|
||||
通过一个返回值来处理异常
|
||||
|
||||
```go
|
||||
resp, err := func() (resp, error)
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func main() {
|
||||
// 变量目录, 打印了文件名称
|
||||
err := walkDir("./xxxx", func(filePath string) {
|
||||
fmt.Println(filePath)
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 最常见的就是 遍历目录里面的文件
|
||||
func walkDir(path string, fn func(string)) error {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 读取path目录下的文件和子目录
|
||||
for _, file := range files {
|
||||
// 如果是目录, 继续往下找
|
||||
if file.IsDir() {
|
||||
walkDir(path+"/"+file.Name(), fn)
|
||||
} else {
|
||||
// 文件, 调用fn函数处理
|
||||
fn(path + "/" + file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 怎么抛出error
|
||||
|
||||
1. 直接返回 底层的error到上层 (return error)
|
||||
```go
|
||||
return err
|
||||
```
|
||||
|
||||
2. 通过error包来New一个error (errors.New)
|
||||
```go
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return errors.New(fmt.Sprintf("File Read Error: %s", err))
|
||||
}
|
||||
```
|
||||
|
||||
2. 直接封装下,封装成自己的error(fmt.Errorf)
|
||||
```go
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("File Read Error: %s", err)
|
||||
}
|
||||
```
|
||||
|
||||
3. 组合异常,再底层的异常上,添加自己定义的异常(errors.Join)
|
||||
```go
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return errors.Join(errors.New("walkDir error"), err)
|
||||
}
|
||||
```
|
||||
|
||||
### 异常的比较 (使用 errors.Is)
|
||||
|
||||
```go
|
||||
var (
|
||||
ErrInvalid = errors.New("invalid argument")
|
||||
ErrPermission = errors.New("permission denied")
|
||||
ErrExist = errors.New("file already exists")
|
||||
ErrNotExist = errors.New("file does not exist")
|
||||
ErrClosed = errors.New("file already closed")
|
||||
)
|
||||
```
|
||||
|
||||
```go
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
fmt.Println(ErrFileNotFound)
|
||||
} else {
|
||||
fmt.Printf("%s", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
```
|
||||
|
||||
## panic (程序崩溃)
|
||||
|
||||
### pannic
|
||||
|
||||
非常危险,空指针(NPE):
|
||||
|
||||
windows蓝屏这种, 特别危险的操作, 如果继续运行可能会造成安全隐患, 内存的非法访问, A程序的内存(内存地址A, 用完后把内存地址释放, OS 这一个物理内存 分配给了程序B, A再次访问已经释放的内存地址A), 崩溃的其中一个场景,内存非发访问
|
||||
|
||||
go 是可以 绕开指针类型系统(nil), 直接使用unsafe包,访问地址地址
|
||||
|
||||
```go
|
||||
// pannic
|
||||
// 索引4 就是非法内存
|
||||
arrs := []int{1, 2, 3}
|
||||
fmt.Println(arrs[4])
|
||||
```
|
||||
|
||||
```sh
|
||||
panic: runtime error: index out of range [4] with length 3
|
||||
|
||||
goroutine 1 [running]:
|
||||
main.main()
|
||||
/Users/yumaojun/Projects/go-course/go20/day03/error/main.go:28 +0x44
|
||||
exit status 2
|
||||
```
|
||||
|
||||
### recover: 从崩溃中恢复
|
||||
|
||||
你写一个API Server, 不小心 panic: runtime error: index out of range [4] with length 3, 程序崩溃,无法继续处理用户请求,你网站有1000个接口,产生这个报错的是其中一个接口的一个流程,需要阻止程序崩溃,打印日志就行
|
||||
|
||||
捕获panic信号, 捕获是一个特殊逻辑,程序退出前 去检查有没有panic信号的, 放执行前面和后面都不行,需要放到 函数调用结束后执行(Hook)
|
||||
```go
|
||||
func recoverHandler() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("Recovered from panic:", r)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer recoverHandler()
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
## defer (函数Hook)
|
||||
|
||||
函数执行完成后,调用你的defer 函数进行 资源清理或者崩溃恢复
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,69 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var ErrFileNotFound = errors.New("file not found")
|
||||
|
||||
func recoverHandler() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("Recovered from panic:", r)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
defer recoverHandler()
|
||||
|
||||
// if r := recover(); r != nil {
|
||||
// fmt.Println("Recovered from panic:", r)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// 变量目录, 打印了文件名称
|
||||
err := walkDir("./xxxx", func(filePath string) {
|
||||
fmt.Println(filePath)
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
fmt.Println("not exist", err)
|
||||
} else {
|
||||
fmt.Printf("%s", err)
|
||||
}
|
||||
// os.Exit(1)
|
||||
}
|
||||
|
||||
// pannic
|
||||
arrs := []int{1, 2, 3}
|
||||
fmt.Println(arrs[4])
|
||||
|
||||
// if r := recover(); r != nil {
|
||||
// fmt.Println("Recovered from panic:", r)
|
||||
// os.Exit(1)
|
||||
// }
|
||||
}
|
||||
|
||||
// 最常见的就是 遍历目录里面的文件
|
||||
func walkDir(path string, fn func(string)) error {
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil {
|
||||
return errors.Join(errors.New("walkDir error"), err)
|
||||
}
|
||||
|
||||
// 读取path目录下的文件和子目录
|
||||
for _, file := range files {
|
||||
// 如果是目录, 继续往下找
|
||||
if file.IsDir() {
|
||||
walkDir(path+"/"+file.Name(), fn)
|
||||
} else {
|
||||
// 文件, 调用fn函数处理
|
||||
fn(path + "/" + file.Name())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
87
day03/error/mpg.drawio
Normal file
87
day03/error/mpg.drawio
Normal file
@ -0,0 +1,87 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="sOwmlNTTQTTo7f71Gu_9" name="第 1 页">
|
||||
<mxGraphModel dx="1134" dy="585" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="2" value="CPU(物理核心, 超线程&lt;逻辑处理核心&gt;)" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="180" width="620" height="290" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="处理核心" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="190" y="350" width="130" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="<span style="color: rgb(0, 0, 0);">处理核心</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="580" y="350" width="130" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" value="OS 进程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="190" y="210" width="540" height="90" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="线程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="214" y="235" width="100" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="8" value="线程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="510" y="235" width="100" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="9" value="线程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="610" y="235" width="100" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="线程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="314" y="235" width="100" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="11" target="8">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="19" value="调度到操作系统的线程上去运行" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="18">
|
||||
<mxGeometry x="-0.2478" y="1" relative="1" as="geometry">
|
||||
<mxPoint as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="11" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="30" width="620" height="110" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" value="OS" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="275" width="60" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" value="Runtime" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="70" width="60" height="30" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" value="协程" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="204" y="70" width="116" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="15" value="<span style="color: rgb(0, 0, 0);">协程</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="70" width="116" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" value="<span style="color: rgb(0, 0, 0);">协程</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="436" y="70" width="116" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" value="<span style="color: rgb(0, 0, 0);">协程</span>" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="552" y="70" width="116" height="40" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="20" value="main()" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="286" y="590" width="150" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="22" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="21" target="20">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="450" y="620" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="24" style="edgeStyle=none;html=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;" edge="1" parent="1" source="21">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<mxPoint x="440" y="690" as="targetPoint"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="25" value="defer hook" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="24">
|
||||
<mxGeometry x="0.1663" y="4" relative="1" as="geometry">
|
||||
<mxPoint as="offset"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="21" value="go runtime" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="570" y="570" width="200" height="120" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="23" value="recover" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="289" y="660" width="150" height="60" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
Loading…
x
Reference in New Issue
Block a user