Skip to content

Go笔记(三):测试、反射、json解析等

单元测试

  1. 文件 _test 结尾,方法名 Test 开头
  • Fail,Error: 测试失败,该测试继续,其他测试也继续执行
  • FailNow, Fatal: 该测试失败,该测试终止,其他测试继续执行

代码覆盖率
go test -v -cover
断言:
https://github.com/stretchr/testify

go
func TestForSqure(t *testing.T) {  
inputs := [...]int{1, 2, 3}  
expected := [...]int{1, 4, 9}  
for i := 0; i < len(inputs); i++ {  
ret := square(inputs[i])  
if ret != expected[i] {  
t.Errorf("input %d expected %d, the actual is %d",  
inputs[i], expected[i], ret)  
}  
}  
}  
  
func square(i int) int {  
return i * i  
}  
  
func TestErrorInCode(t *testing.T) {  
fmt.Println("Start")  
t.Error("Error")  
fmt.Println("End")  
}  
  
func TestFailInCode(t *testing.T) {  
fmt.Println("Start")  
t.Fatal("Error") //该测试失败,该测试终止,其他测试继续  
fmt.Println("End") //不会执行本行  
}
func TestForSqure(t *testing.T) {  
inputs := [...]int{1, 2, 3}  
expected := [...]int{1, 4, 9}  
for i := 0; i < len(inputs); i++ {  
ret := square(inputs[i])  
if ret != expected[i] {  
t.Errorf("input %d expected %d, the actual is %d",  
inputs[i], expected[i], ret)  
}  
}  
}  
  
func square(i int) int {  
return i * i  
}  
  
func TestErrorInCode(t *testing.T) {  
fmt.Println("Start")  
t.Error("Error")  
fmt.Println("End")  
}  
  
func TestFailInCode(t *testing.T) {  
fmt.Println("Start")  
t.Fatal("Error") //该测试失败,该测试终止,其他测试继续  
fmt.Println("End") //不会执行本行  
}

BenchNow 性能测评

运行所有文件 go test -bench=. [-benchmenm (分析内存大小,allocs 内存个数)]
运行方法 go test -bench=方法名 [-benchmenm (分析内存大小,allocs 内存个数)]

go
func BenchmarkXX(b *testing.B) {  
//性能无关的代码  
//....  
b.ResetTimer()  
for i := 0; i < b.N; i++ {  
  
}  
b.StopTimer()  
//性能无关的代码  
//...  
}
func BenchmarkXX(b *testing.B) {  
//性能无关的代码  
//....  
b.ResetTimer()  
for i := 0; i < b.N; i++ {  
  
}  
b.StopTimer()  
//性能无关的代码  
//...  
}

BDD (Behavior Driven Developement)

BDD in Go
地址: https://github.com/smartystreets/goconvey
安装:

bash
go get -u github.com/smartystreets/goconvey
go get -u github.com/smartystreets/goconvey

启动 Web ui:

$GOPATH/bin/goconvey

反射 基础操作

反射

反射类型 reflect.TypeOf
反射值 reflect.ValueOf

  • reflect.TypeOf 返回类型(reflect.Type)
  • reflect.ValueOf 返回值 (reflect.Value)
  • 可以从 reflect.Value 获得类型
  • 通过 kind 来判断类型(枚举)
go
func CheckType(v interface{}) {  
t := reflect.TypeOf(v)  
switch t.Kind() {  
case reflect.Float32, reflect.Float64:  
fmt.Println("Float")  
case reflect.Int, reflect.Int32, reflect.Int64:  
fmt.Println("Integer")  
case reflect.Ptr:  
fmt.Println("Ptr")  
default:  
fmt.Println("other type")  
}  
}  
  
func TestBaseType(t *testing.T) {  
var f float64 = 1  
CheckType(f) // Float  
CheckType(&f) // Ptr  
}  
  
func TestTypeAndValue(t *testing.T) {  
var f int64 = 10  
t.Log(reflect.TypeOf(f), reflect.ValueOf(f)) // int64 10  
t.Log(reflect.ValueOf(f).Type()) //int64  
}
func CheckType(v interface{}) {  
t := reflect.TypeOf(v)  
switch t.Kind() {  
case reflect.Float32, reflect.Float64:  
fmt.Println("Float")  
case reflect.Int, reflect.Int32, reflect.Int64:  
fmt.Println("Integer")  
case reflect.Ptr:  
fmt.Println("Ptr")  
default:  
fmt.Println("other type")  
}  
}  
  
func TestBaseType(t *testing.T) {  
var f float64 = 1  
CheckType(f) // Float  
CheckType(&f) // Ptr  
}  
  
func TestTypeAndValue(t *testing.T) {  
var f int64 = 10  
t.Log(reflect.TypeOf(f), reflect.ValueOf(f)) // int64 10  
t.Log(reflect.ValueOf(f).Type()) //int64  
}

通过字符串的形式调用类型的某个方法

按名字访问结构的成员
reflect.ValueOf(*e).FieldByName("Name")

按名字访问结构的方法
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(1)})

go
type EmployeeT struct {  
EmployeeId string  
Name string `format:"normal"` //struct tag ,类似java的注解  
Age int  
}  
  
func (e *EmployeeT) UpdateAge(newVal int) {  
e.Age = newVal  
}  
  
func TestInvokeByName(t *testing.T) {  
e := &EmployeeT{"1", "Mike", 30}  
// 按名字获取成员  
t.Logf("Name:value(%[1]v), Type(%[1]T)",reflect.ValueOf(*e).FieldByName("EmployeeId")) // Name:value(1), Type(reflect.Value)  
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {  
t.Error("Failed to get 'name' field")  
} else {  
// 通过反射获取struct tag  
t.Log("Tag: format", nameField.Tag.Get("format")) // Tag: format normal  
}  
  
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(99)})  
t.Log("update age:", e) // update age: &{1 Mike 99} age更新为99  
}
type EmployeeT struct {  
EmployeeId string  
Name string `format:"normal"` //struct tag ,类似java的注解  
Age int  
}  
  
func (e *EmployeeT) UpdateAge(newVal int) {  
e.Age = newVal  
}  
  
func TestInvokeByName(t *testing.T) {  
e := &EmployeeT{"1", "Mike", 30}  
// 按名字获取成员  
t.Logf("Name:value(%[1]v), Type(%[1]T)",reflect.ValueOf(*e).FieldByName("EmployeeId")) // Name:value(1), Type(reflect.Value)  
if nameField, ok := reflect.TypeOf(*e).FieldByName("Name"); !ok {  
t.Error("Failed to get 'name' field")  
} else {  
// 通过反射获取struct tag  
t.Log("Tag: format", nameField.Tag.Get("format")) // Tag: format normal  
}  
  
reflect.ValueOf(e).MethodByName("UpdateAge").Call([]reflect.Value{reflect.ValueOf(99)})  
t.Log("update age:", e) // update age: &{1 Mike 99} age更新为99  
}

反射 万能程序

主要列举几个应用反射的例子。

DeepEqual

DeepEqual 比较切片和 map

go
func TestDeepEqual(t *testing.T) {  
a := map[int]string{1: "one", 2: "two", 3: "three"}  
b := map[int]string{1: "two", 2: "two", 4: "three"}  
//fmt.Println(a == b) // 编译不通过  
fmt.Println(reflect.DeepEqual(a, b)) //false  
  
s1 := []int{1, 2, 3}  
s2 := []int{1, 2, 3}  
s3 := []int{1, 2, 4}  
t.Log("s1 == s2 ? ", reflect.DeepEqual(s1, s2)) // true  
t.Log("s1 == s3 ? ", reflect.DeepEqual(s1, s3)) // false  
  
}
func TestDeepEqual(t *testing.T) {  
a := map[int]string{1: "one", 2: "two", 3: "three"}  
b := map[int]string{1: "two", 2: "two", 4: "three"}  
//fmt.Println(a == b) // 编译不通过  
fmt.Println(reflect.DeepEqual(a, b)) //false  
  
s1 := []int{1, 2, 3}  
s2 := []int{1, 2, 3}  
s3 := []int{1, 2, 4}  
t.Log("s1 == s2 ? ", reflect.DeepEqual(s1, s2)) // true  
t.Log("s1 == s3 ? ", reflect.DeepEqual(s1, s3)) // false  
  
}

结构体填充

go
type Customer struct {  
CookieID string  
Name string  
Age int  
}  
  
/*  
结构体填充  
*/  
func fillBySettings(st interface{}, settings map[string]interface{}) error {  
  
// func (v Value) Elem() Value  
// Elem returns the value that the interface v contains or that the pointer v points to.  
// It panics if v's Kind is not Interface or Ptr.  
// It returns the zero Value if v is nil.  
  
if reflect.TypeOf(st).Kind() != reflect.Ptr {  
return errors.New("the first param should be a pointer to the struct type.")  
}  
// Elem() 获取指针指向的值  
if reflect.TypeOf(st).Elem().Kind() != reflect.Struct {  
return errors.New("the first param should be a pointer to the struct type.")  
}  
  
if settings == nil {  
return errors.New("settings is nil.")  
}  
  
var (  
field reflect.StructField  
ok bool  
)  
  
for k, v := range settings {  
// 检查类型里有没有field (因为是指针类型,需要用Elem(),获取指针类型的)  
if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {  
continue  
}  
if field.Type == reflect.TypeOf(v) {  
vstr := reflect.ValueOf(st)  
vstr = vstr.Elem()  
vstr.FieldByName(k).Set(reflect.ValueOf(v))  
}  
  
}  
return nil  
}  
  
func TestFillNameAndAge(t *testing.T) {  
settings := map[string]interface{}{"Name": "Mike", "Age": 30}  
e := Employee{}  
if err := fillBySettings(&e, settings); err != nil {  
t.Fatal(err)  
}  
t.Log(e)  
c := new(Customer)  
if err := fillBySettings(c, settings); err != nil {  
t.Fatal(err)  
}  
t.Log(*c)  
}
type Customer struct {  
CookieID string  
Name string  
Age int  
}  
  
/*  
结构体填充  
*/  
func fillBySettings(st interface{}, settings map[string]interface{}) error {  
  
// func (v Value) Elem() Value  
// Elem returns the value that the interface v contains or that the pointer v points to.  
// It panics if v's Kind is not Interface or Ptr.  
// It returns the zero Value if v is nil.  
  
if reflect.TypeOf(st).Kind() != reflect.Ptr {  
return errors.New("the first param should be a pointer to the struct type.")  
}  
// Elem() 获取指针指向的值  
if reflect.TypeOf(st).Elem().Kind() != reflect.Struct {  
return errors.New("the first param should be a pointer to the struct type.")  
}  
  
if settings == nil {  
return errors.New("settings is nil.")  
}  
  
var (  
field reflect.StructField  
ok bool  
)  
  
for k, v := range settings {  
// 检查类型里有没有field (因为是指针类型,需要用Elem(),获取指针类型的)  
if field, ok = (reflect.ValueOf(st)).Elem().Type().FieldByName(k); !ok {  
continue  
}  
if field.Type == reflect.TypeOf(v) {  
vstr := reflect.ValueOf(st)  
vstr = vstr.Elem()  
vstr.FieldByName(k).Set(reflect.ValueOf(v))  
}  
  
}  
return nil  
}  
  
func TestFillNameAndAge(t *testing.T) {  
settings := map[string]interface{}{"Name": "Mike", "Age": 30}  
e := Employee{}  
if err := fillBySettings(&e, settings); err != nil {  
t.Fatal(err)  
}  
t.Log(e)  
c := new(Customer)  
if err := fillBySettings(c, settings); err != nil {  
t.Fatal(err)  
}  
t.Log(*c)  
}

反射 unsafe poiner

不安全编程的危险性

指针可以转换成任意类型的指针,利用它来实现类型转换. 非常危险

go
i := 10  
f := *(float64)(unsafe.Pointer(&i))  
  
func TestUnsafe(t *testing.T) {  
i := 10  
f := *(*float64)(unsafe.Pointer(&i))  
t.Log(unsafe.Pointer(&i)) // 0xc000016298  
t.Log(f) // 5e-323  
}
i := 10  
f := *(float64)(unsafe.Pointer(&i))  
  
func TestUnsafe(t *testing.T) {  
i := 10  
f := *(*float64)(unsafe.Pointer(&i))  
t.Log(unsafe.Pointer(&i)) // 0xc000016298  
t.Log(f) // 5e-323  
}

atomic 包 进行指针的读写操作

go
//原子类型操作  
func TestAtomic(t *testing.T) {  
var shareBufPtr unsafe.Pointer  
writeDataFn := func() {  
data := []int{}  
for i := 0; i < 100; i++ {  
data = append(data, i)  
}  
atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))  
}  
readDataFn := func() {  
data := atomic.LoadPointer(&shareBufPtr)  
fmt.Println(data, *(*[]int)(data))  
}  
var wg sync.WaitGroup  
writeDataFn()  
for i := 0; i < 10; i++ {  
wg.Add(1)  
go func() {  
for i := 0; i < 10; i++ {  
writeDataFn()  
time.Sleep(time.Microsecond * 100)  
}  
wg.Done()  
}()  
wg.Add(1)  
go func() {  
for i := 0; i < 10; i++ {  
readDataFn()  
time.Sleep(time.Microsecond * 100)  
}  
wg.Done()  
}()  
}  
wg.Wait()  
}
//原子类型操作  
func TestAtomic(t *testing.T) {  
var shareBufPtr unsafe.Pointer  
writeDataFn := func() {  
data := []int{}  
for i := 0; i < 100; i++ {  
data = append(data, i)  
}  
atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))  
}  
readDataFn := func() {  
data := atomic.LoadPointer(&shareBufPtr)  
fmt.Println(data, *(*[]int)(data))  
}  
var wg sync.WaitGroup  
writeDataFn()  
for i := 0; i < 10; i++ {  
wg.Add(1)  
go func() {  
for i := 0; i < 10; i++ {  
writeDataFn()  
time.Sleep(time.Microsecond * 100)  
}  
wg.Done()  
}()  
wg.Add(1)  
go func() {  
for i := 0; i < 10; i++ {  
readDataFn()  
time.Sleep(time.Microsecond * 100)  
}  
wg.Done()  
}()  
}  
wg.Wait()  
}

架构

Pip-Filter 架构

适合数据处理,数据分析

20200131158048235954296.png

相当于一个个的 filter,对数据进行处理。

  • 非常适合与数据处理及数据分析系统
  • Filter 疯转数据处理的功能
  • 松耦合:Filter 只和数据(格式)耦合
  • Pipe 用于连接 Filter 传递数据或者在异步处理过程中缓冲数据流
  • 进程内同步调用时,pipe 演变为数据在方法调用间传递

20200131158048264844481.png
20200131158048265315848.png

代码示例

源码文件见 quicktouch/go-learn

架构模式 micro kernel

2020020115805305425749.png

特点:

  • 易于扩展
  • 错误隔离
  • 保持架构一致性

要点

  • 内核包含公共流程或通用逻辑
  • 将可变或可扩展部门规划为扩展点
  • 抽象扩展行为,定义接口
  • 利用插件进行扩展

json 解析

内置的 json 解析

  • 利用反射实现,通过 FieldTag 来标识对应的 json 值

因为利用了反射,性能不行,一般可用于配置文件的解析

go
type BasicInfo struct {  
Name string `json:"name"`  
Age int `json:"age"`  
}  
  
type JobInfo struct {  
Skills []string `json:"skills"`  
}  
  
type EmployeeStruct struct {  
BasicInfo BasicInfo `json:"basic_info"`  
JobInfo JobInfo `json:"job_info"`  
}  
  
var jsonStr = `{  
"basic_info": {  
"name":"Mike",  
"age":30  
},  
"job_info": {  
"skills":["Java","Go","C"]  
}  
}`  
  
func TestEmbeddedJson(t *testing.T) {  
e := new(EmployeeStruct)  
// json 赋值给struct  
err := json.Unmarshal([]byte(jsonStr), e)  
if err != nil {  
t.Error(err) // {{Mike 30} {[Java Go C]}}  
}  
fmt.Println(*e)  
// struct反序列化  
if v, err := json.Marshal(e); err == nil {  
fmt.Println(string(v))  
// {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}  
} else {  
t.Error(err)  
}  
}
type BasicInfo struct {  
Name string `json:"name"`  
Age int `json:"age"`  
}  
  
type JobInfo struct {  
Skills []string `json:"skills"`  
}  
  
type EmployeeStruct struct {  
BasicInfo BasicInfo `json:"basic_info"`  
JobInfo JobInfo `json:"job_info"`  
}  
  
var jsonStr = `{  
"basic_info": {  
"name":"Mike",  
"age":30  
},  
"job_info": {  
"skills":["Java","Go","C"]  
}  
}`  
  
func TestEmbeddedJson(t *testing.T) {  
e := new(EmployeeStruct)  
// json 赋值给struct  
err := json.Unmarshal([]byte(jsonStr), e)  
if err != nil {  
t.Error(err) // {{Mike 30} {[Java Go C]}}  
}  
fmt.Println(*e)  
// struct反序列化  
if v, err := json.Marshal(e); err == nil {  
fmt.Println(string(v))  
// {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}  
} else {  
t.Error(err)  
}  
}

EasyJson

采用代码生成而非反射.

安装: go get -u github.com/mailru/easyjson/...

使用 EasyJson 生成对应的解析方法文件(struct 文件需要在 gopath 下):

~/go/bin/easyjson -all /path/to/fileName.go

go
var jsonStr = `{  
"basic_info": {  
"name":"Mike",  
"age":30  
},  
"job_info": {  
"skills":["Java","Go","C"]  
}  
}`  
  
func TestEasyJson(t *testing.T) {  
e := EmployeeStruct{}  
err := e.UnmarshalJSON([]byte(jsonStr))  
if err != nil {  
t.Error(err)  
} else {  
fmt.Println(e) // {{Mike 30} {[Java Go C]}}  
}  
  
if v, err := e.MarshalJSON(); err != nil {  
t.Error(err)  
} else {  
fmt.Println(string(v)) // {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}  
}  
}
var jsonStr = `{  
"basic_info": {  
"name":"Mike",  
"age":30  
},  
"job_info": {  
"skills":["Java","Go","C"]  
}  
}`  
  
func TestEasyJson(t *testing.T) {  
e := EmployeeStruct{}  
err := e.UnmarshalJSON([]byte(jsonStr))  
if err != nil {  
t.Error(err)  
} else {  
fmt.Println(e) // {{Mike 30} {[Java Go C]}}  
}  
  
if v, err := e.MarshalJSON(); err != nil {  
t.Error(err)  
} else {  
fmt.Println(string(v)) // {"basic_info":{"name":"Mike","age":30},"job_info":{"skills":["Java","Go","C"]}}  
}  
}

性能测试

使用go test -bench=.进行性能测试

go
func BenchmarkEasyJson(b *testing.B) {  
b.ResetTimer()  
e := EmployeeStruct{}  
for i := 0; i < b.N; i++ {  
err := e.UnmarshalJSON([]byte(jsonStr))  
if err != nil {  
b.Error(err)  
}  
if _, err := e.MarshalJSON(); err != nil {  
b.Error(err)  
}  
}  
}  
  
func BenchmarkEmbedJson(b *testing.B) {  
b.ResetTimer()  
e := new(EmployeeStruct)  
for i := 0; i < b.N; i++ {  
// json 赋值给struct  
err := json.Unmarshal([]byte(jsonStr), e)  
if err != nil {  
b.Error(err)  
}  
if _, err := json.Marshal(e); err != nil {  
b.Error(err)  
}  
}  
}  
  
// go test -bench=.  
  
//goos: darwin  
//goarch: amd64  
//BenchmarkEasyJson-8 1262138 (运行次数) 936 ns/op (每次运行的时间/纳秒)  
//BenchmarkEmbedJson-8 334260 3494 ns/op
func BenchmarkEasyJson(b *testing.B) {  
b.ResetTimer()  
e := EmployeeStruct{}  
for i := 0; i < b.N; i++ {  
err := e.UnmarshalJSON([]byte(jsonStr))  
if err != nil {  
b.Error(err)  
}  
if _, err := e.MarshalJSON(); err != nil {  
b.Error(err)  
}  
}  
}  
  
func BenchmarkEmbedJson(b *testing.B) {  
b.ResetTimer()  
e := new(EmployeeStruct)  
for i := 0; i < b.N; i++ {  
// json 赋值给struct  
err := json.Unmarshal([]byte(jsonStr), e)  
if err != nil {  
b.Error(err)  
}  
if _, err := json.Marshal(e); err != nil {  
b.Error(err)  
}  
}  
}  
  
// go test -bench=.  
  
//goos: darwin  
//goarch: amd64  
//BenchmarkEasyJson-8 1262138 (运行次数) 936 ns/op (每次运行的时间/纳秒)  
//BenchmarkEmbedJson-8 334260 3494 ns/op

http 示例

自带的 http

url 匹配:

  • URL 分为两种,末尾是 /: 表示一个子树,后面可以跟其他子路径;末尾不是/,表示一个叶子,固定的路径。
  • / 结尾的 url 可以匹配它的任何子路径,比如 /images 会匹配到 /images/1.jpg
  • 采用最长匹配原则,如果有多个匹配,一定采用匹配路径最长的那个进行处理
  • 如果没有找到任何匹配项,会返回 404 错误
go
package main  
  
import (  
"fmt"  
"net/http"  
"time"  
)  
  
func main() {  
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
fmt.Fprintf(w, "hello world") // 返回给客户端  
})  
// 可以匹配 time/*http.HandleFunc("/time/", func(w http.ResponseWriter, r *http.Request) {  
t := time.Now()  
timeStr := fmt.Sprintf("{\"time\":\"%s\"}", t)  
_, _ = w.Write([]byte(timeStr))  
})  
_ = http.ListenAndServe(":8080", nil)  
}
package main  
  
import (  
"fmt"  
"net/http"  
"time"  
)  
  
func main() {  
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
fmt.Fprintf(w, "hello world") // 返回给客户端  
})  
// 可以匹配 time/*http.HandleFunc("/time/", func(w http.ResponseWriter, r *http.Request) {  
t := time.Now()  
timeStr := fmt.Sprintf("{\"time\":\"%s\"}", t)  
_, _ = w.Write([]byte(timeStr))  
})  
_ = http.ListenAndServe(":8080", nil)  
}

第三方框架

https://github.com/julienschmidt/httprouter

更简单,也方便构建 RESTful

go get -u github.com/julienschmidt/httprouter

示例:

go
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {  
fmt.Fprint(w, "welcome!")  
}  
  
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {  
fmt.Fprintf(w, "hello, %s", ps.ByName("name"))  
}  
  
func main() {  
router := httprouter.New()  
router.GET("/", Index)  
router.GET("/hello/:name", Hello)  
log.Fatal(http.ListenAndServe(":8080", router))  
}  
// http://localhost:8080/hello/google -> hello, google
func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {  
fmt.Fprint(w, "welcome!")  
}  
  
func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {  
fmt.Fprintf(w, "hello, %s", ps.ByName("name"))  
}  
  
func main() {  
router := httprouter.New()  
router.GET("/", Index)  
router.GET("/hello/:name", Hello)  
log.Fatal(http.ListenAndServe(":8080", router))  
}  
// http://localhost:8080/hello/google -> hello, google

性能分析 工具

相关依赖安装

安装 graphviz 添加图形的支持

brew install graphviz

安装 go-torch

  1. go get github.com/uber/go-torch
  2. git clone https://github.com/brendangregg/FlameGraph.git 将 flamegraph.pl 拷贝到/usr/local/bin目录下
  3. 输入 flamegraph.pl -h 可以看到是否成功

通过文件方式输出 Profile

可以输出 cpu 内存 线程数等。

相关文档查看:

编译测试代码:

bash
# 编译测试代码  
go build prof.go  
# 运行二进制, 运行完多了 cpu.prof goroutine.prof mem.prof 这几个文件  
./prof
# 编译测试代码  
go build prof.go  
# 运行二进制, 运行完多了 cpu.prof goroutine.prof mem.prof 这几个文件  
./prof

查看这些文件 (适合短时间运行的程序)

go tool pprof 编译出的二进制 cpu.prof/goroutine.prof/mem.prof

例如:

bash
go tool pprof prof cpu.prof  
# log 如下,  
# 随后使用top命令可以看到cpu详情。 flat表示所占时间和所占比例。 cum/cum%表示这个函数还调用了别的函数,总体加和在一起所占的时间和比例。  
# 使用list + 符号名 命令可看具体哪行的耗时  
# svg 以图像的方式查看调用关系及具体的cpu耗时,比较直观。  
# exit 退出  
  
File: profType: cpuTime: Feb 2, 2020 at 10:44pm (CST)  
Duration: 2.06s, Total samples = 1.81s (87.91%)  
Entering interactive mode (type "help" for commands, "o" for options)  
(pprof) top  
Showing nodes accounting for 1.81s, 100% of 1.81s totalShowing top 10 nodes out of 12flat flat% sum% cum cum%1.77s 97.79% 97.79% 1.78s 98.34% main.fillMatrix0.02s 1.10% 98.90% 0.02s 1.10% main.calculate0.01s 0.55% 99.45% 0.01s 0.55% math/rand.(*rngSource).Int63  
0.01s 0.55% 100% 0.01s 0.55% runtime.newstack0 0% 100% 1.81s 100% main.main0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31n  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int63  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Intn  
0 0% 100% 0.01s 0.55% os.Create(pprof) list fillMatrixTotal: 1.81sROUTINE ======================== main.fillMatrix in /Users/panda/Desktop/go-learn/playground/src/lession/tools/file/prof.go1.77s 1.78s (flat, cum) 98.34% of Total  
. . 15:  
. . 16:func fillMatrix(m *[row][col]int) {  
. . 17: s := rand.New(rand.NewSource(time.Now().UnixNano()))  
. . 18: for i := 0; i < row; i++ {  
. . 19: for j := 0; j < col; j++ {  
1.77s 1.78s 20: m[i][j] = s.Intn(100000)  
. . 21: }  
. . 22: }  
. . 23:}  
. . 24:  
. . 25:func calculate(m *[row][col]int) {  
(pprof) exit
go tool pprof prof cpu.prof  
# log 如下,  
# 随后使用top命令可以看到cpu详情。 flat表示所占时间和所占比例。 cum/cum%表示这个函数还调用了别的函数,总体加和在一起所占的时间和比例。  
# 使用list + 符号名 命令可看具体哪行的耗时  
# svg 以图像的方式查看调用关系及具体的cpu耗时,比较直观。  
# exit 退出  
  
File: profType: cpuTime: Feb 2, 2020 at 10:44pm (CST)  
Duration: 2.06s, Total samples = 1.81s (87.91%)  
Entering interactive mode (type "help" for commands, "o" for options)  
(pprof) top  
Showing nodes accounting for 1.81s, 100% of 1.81s totalShowing top 10 nodes out of 12flat flat% sum% cum cum%1.77s 97.79% 97.79% 1.78s 98.34% main.fillMatrix0.02s 1.10% 98.90% 0.02s 1.10% main.calculate0.01s 0.55% 99.45% 0.01s 0.55% math/rand.(*rngSource).Int63  
0.01s 0.55% 100% 0.01s 0.55% runtime.newstack0 0% 100% 1.81s 100% main.main0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int31n  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Int63  
0 0% 100% 0.01s 0.55% math/rand.(*Rand).Intn  
0 0% 100% 0.01s 0.55% os.Create(pprof) list fillMatrixTotal: 1.81sROUTINE ======================== main.fillMatrix in /Users/panda/Desktop/go-learn/playground/src/lession/tools/file/prof.go1.77s 1.78s (flat, cum) 98.34% of Total  
. . 15:  
. . 16:func fillMatrix(m *[row][col]int) {  
. . 17: s := rand.New(rand.NewSource(time.Now().UnixNano()))  
. . 18: for i := 0; i < row; i++ {  
. . 19: for j := 0; j < col; j++ {  
1.77s 1.78s 20: m[i][j] = s.Intn(100000)  
. . 21: }  
. . 22: }  
. . 23:}  
. . 24:  
. . 25:func calculate(m *[row][col]int) {  
(pprof) exit

go-torch

go-torch 查看火炬图。

bash
go-torch cpu.prof```  
  
### 以 http 的方式输出 profile  
  
- 简单,适合持续性运行的程序  
- 应用程序中导入`import _ "net/http/pprof"`, 并启动 http server 即可  
- `http://<host>:<port>/debug/pprof/`  
- `go tool pprof http://<host>:<port>/debug/pprof/profile?seconds-10` (默认 30s)  
- `go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile`  
  
  
```go  
import (  
"fmt"  
"net/http"  
_ "net/http/pprof"  
)  
func GetFibonacciSeries(n int) ([]int, error) {  
fibList := []int{1, 1}  
for i := 2; i < n; i++ {  
fibList = append(fibList, fibList[i-2]+fibList[i-1])  
}  
return fibList, nil  
}  
func main() {  
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
w.Write([]byte("welcome!"))  
})  
http.HandleFunc("/fb", func(w http.ResponseWriter, r *http.Request) {  
var fabs []int  
for i := 0; i < 100000; i++ {  
fabs, _ = GetFibonacciSeries(50)  
}  
w.Write([]byte(fmt.Sprintf("%v", fabs)))  
})  
_ = http.ListenAndServe(":8080", nil)  
}  
/*  
访问 http://localhost:8080/debug/pprof/ 可看到一些分析  
  
点击profile会进行30秒的采样,并下载文件。  
  
也能用命令行  
  
go tool pprof http://<host>:<port>/debug/pprof/profile?seconds=10  
  
然后使用 top命令 (top排序,如按cum排序: top -cum)  
命令: list 方法名  
等。  
*/
go-torch cpu.prof```  
  
### 以 http 的方式输出 profile  
  
- 简单,适合持续性运行的程序  
- 应用程序中导入`import _ "net/http/pprof"`, 并启动 http server 即可  
- `http://<host>:<port>/debug/pprof/`  
- `go tool pprof http://<host>:<port>/debug/pprof/profile?seconds-10` (默认 30s)  
- `go-torch -seconds 10 http://<host>:<port>/debug/pprof/profile`  
  
  
```go  
import (  
"fmt"  
"net/http"  
_ "net/http/pprof"  
)  
func GetFibonacciSeries(n int) ([]int, error) {  
fibList := []int{1, 1}  
for i := 2; i < n; i++ {  
fibList = append(fibList, fibList[i-2]+fibList[i-1])  
}  
return fibList, nil  
}  
func main() {  
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
w.Write([]byte("welcome!"))  
})  
http.HandleFunc("/fb", func(w http.ResponseWriter, r *http.Request) {  
var fabs []int  
for i := 0; i < 100000; i++ {  
fabs, _ = GetFibonacciSeries(50)  
}  
w.Write([]byte(fmt.Sprintf("%v", fabs)))  
})  
_ = http.ListenAndServe(":8080", nil)  
}  
/*  
访问 http://localhost:8080/debug/pprof/ 可看到一些分析  
  
点击profile会进行30秒的采样,并下载文件。  
  
也能用命令行  
  
go tool pprof http://<host>:<port>/debug/pprof/profile?seconds=10  
  
然后使用 top命令 (top排序,如按cum排序: top -cum)  
命令: list 方法名  
等。  
*/