这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记。
ppt链接高质量编程与性能调优实战
错误和异常处理
简单错误
- 简单的错误是指仅出现一次的错误,切在其他地方不需要捕获该错误
- 优先使用errors.New来创建匿名变量来直接表示简单错误
- 如果有格式化的需求,使用fmt.Errorf
错误的Wrap和Unwrap
- 错误的Wrap实际上是提供了一个error嵌套另一个error的能力,从而生成了一个error的跟踪链
- 在fmt.Errorf中使用:%w关键字来将一个错误关联至错误链中
list,_,err:=c,GetBytes(cache.Subkey(a.actionID,"srcfiles"))
if err!=nil{
return fmt.errorf("reading srcfiles list :%w",err)
}
panic
- 不建议在业务代码中使用panic
- 调用函数不包含recover回早储层程序崩溃
- 若问题可以被屏蔽或解决,建议使用error代替panic
- 当程序启动阶段发生不可逆转的错误时,可以在init或main函数中使用panic
recover
- recover只能在被defer的函数中使用
- 嵌套无法生效
- 只能在goroutine生效
- defer的语句是后进先出
- 如果需要更多的上下文信息,可以在recover后在log中记录当前的调用栈
错误与异常的处理
- error尽可能提供简明的上下文信息链,方便定位问题
- panic 用于真正异常的情况
- recover 生效范围,在当前goroutine的被defer的函数中生效
性能分析工具pprof
golang pprof 实战 | Wolfogre's Blog
PProf 关注的模块
- CPU profile:报告程序的 CPU 使用情况,按照一定频率去采集应用程序在 CPU 和寄存器上面的数据
- Memory Profile(Heap Profile):报告程序的内存使用情况
- Block Profiling:报告 goroutines 不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈
- Goroutine Profiling:报告 goroutines 的使用情况,有哪些 goroutine,它们的调用关系是怎样的 两个包中引入PProf
import "net/http/pprof"
import "runtime/pprof"
其中 net/http/pprof 使用 runtime/pprof 包来进行封装,并在 http 端口上暴露出来。runtime/pprof 可以用来产生 dump 文件,再使用 Go Tool PProf 来分析这运行日志。
使用 net/http/pprof 可以做到直接看到当前 web 服务的状态,包括 CPU 占用情况和内存使用情况等。
如果服务是一直运行的,如 web 应用,可以很方便的使用第一种方式 import "net/http/pprof"
package main
import (
"log"
"net/http"
"sync"
_ "net/http/pprof" // 会自动注册 handler 到 http server,方便通过 http 接口获取程序运行采样报告
"runtime"
// 略
)
func main() {
// 略
runtime.GOMAXPROCS(1) // 限制 CPU 使用数,避免过载
runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪
runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
// 启动一个 http server,注意 pprof 相关的 handler 已经自动注册过了
if err := http.ListenAndServe(":6060", nil); err != nil {
log.Fatal(err)
}
wg.Done()
}()
wg.Wait()
// 略
}
对github上的代码进行了简单的修改,源代码中使用了协程,协程是否运行结束本来是和主线程无关的,所以会导致协程还没运行完主线程结束退出。所以利用等待队列sync.WaitGroup
- 执行命令
go tool pprof http://localhost:6060/debug/pprof/profile进入交互界面
2.执行top命令,查看CPU占用较高的调用
各指标含义
- flat:当前函数本身的执行耗时
- flat%:flat占CPU总时间的比例
- sum%:上面每一行的flat%总和
- cum:指当前函数本身加上其调用函数的总耗时
- cum%:cum占CPU总时间的比例 3.list命令
注:list Eat中Eat时正则表达式 这里认为是Tiger结构体的一个方法 根据指定的正则表达式查找代码行
找出占用CPU 8.14S的代码行
4.web命令
简述Go搭建http服务器
package main
import (
"fmt"
"net/http"
)
func HelloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello World")
}
func main() {
http.HandleFunc("/", HelloHandler)
http.ListenAndServe(":8000", nil)
}
运行代码之后,在浏览器中打开localhost:8000就可以看到Hello World。这段代码先利用http.HandleFunc在根路由/上注册了一个HelloHandler, 然后利用http.ListenAndServe启动服务器并监听本地的 8000 端口。当有请求过来时,则根据路由执行对应的handler函数。
详细请参考:
深入学习用 Go 编写 HTTP 服务器_kevin_tech的博客-CSDN博客
注册路由
import中导包
golang的import表示导入包,其中. 和 _ 分别有特殊含义。 .表示导入以后,该包的函数和变量不需要再直接写入包名称。 的作用就更特殊。当导入一个包的时候,该包的init和其他函数都会被导入;但不是所有函数都需要导 入,这个时候.,可以只用""符号入init,而不需要导入其他函数。 关于包 runtime