• go语言编程之旅笔记6


    第六章: Go中的大杀器

    1. 简介

      介绍了PProf,trace,godebug,gops,metrrics,prometheus等等库来进行性能监控等等的功能

    2. PProf

      1. 使用net/http/pprof可以能方便的采集web服务在运行时的数据,直接import十分简单
      import (
          _ "net/http/pprof"
      )
      
      // 看看pprof的init发现注入了很多handler
      func init() {
      	http.HandleFunc("/debug/pprof/", Index)
      	http.HandleFunc("/debug/pprof/cmdline", Cmdline)
      	http.HandleFunc("/debug/pprof/profile", Profile)
      	http.HandleFunc("/debug/pprof/symbol", Symbol)
      	http.HandleFunc("/debug/pprof/trace", Trace)
      }
      
      

      可以通过浏览器或是交互式终端进行访问,我选择浏览器。-> IP地址/debug/pprof

      浏览器有时效性,真要查问题还是用终端

      go tool pprof ip/debug/pprof/profile?seconds=60
      

      不同的路由对应的项,比如cpu/heap/goroutine等等,这就不贴了。

      采集生成的profile文件也是可以用web方式查阅的,go tool pprof -http=:6000 profile,如果报graphviz的错说明要装组件。

      1. 通过Lookup进行采集,这种方式需要写code,支持6种类型,goroutine,threadcreate,heap,block,mutex
      这种方式需要写code,支持6种类型,goroutine,threadcreate,heap,block,mutex
      package main
      
      import (
      	"io"
      	"net/http"
      	_ "net/http/pprof"
      	"os"
      	"runtime"
      	"runtime/pprof"
      )
      
      //go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60
      
      func main() {
      	http.HandleFunc("/lookup/heap", func(w http.ResponseWriter, r *http.Request) {
      		_ = pprofLookup(LookupHeap, os.Stdout)
      	})
      
      	http.HandleFunc("/lookup/threadcreate", func(w http.ResponseWriter, r *http.Request) {
      		_ = pprofLookup(LookupThreadcreate, os.Stdout)
      	})
      
      	http.HandleFunc("/lookup/block", func(w http.ResponseWriter, r *http.Request) {
      		_ = pprofLookup(LookupBlock, os.Stdout)
      	})
      
      	http.HandleFunc("/lookup/goroutine", func(w http.ResponseWriter, r *http.Request) {
      		_ = pprofLookup(LookupGoroutine, os.Stdout)
      	})
      
      	_ = http.ListenAndServe("0.0.0.0:6060", nil)
      }
      
      type LookupType int8
      
      const (
      	LookupGoroutine LookupType = iota
      	LookupThreadcreate
      	LookupHeap
      	LookupAllocs
      	LookupBlock
      	LookupMutex
      )
      
      func pprofLookup(lookupType LookupType, w io.Writer) error {
      	var err error
      	switch lookupType {
      	case LookupGoroutine:
      		p := pprof.Lookup("goroutine")
      		err = p.WriteTo(w, 2)
      	case LookupThreadcreate:
      		p := pprof.Lookup("threadcreate")
      		err = p.WriteTo(w, 2)
      	case LookupHeap:
      		p := pprof.Lookup("heap")
      		err = p.WriteTo(w, 2)
      	case LookupAllocs:
      		p := pprof.Lookup("allocs")
      		err = p.WriteTo(w, 2)
      	case LookupBlock:
      		p := pprof.Lookup("block")
      		err = p.WriteTo(w, 2)
      	case LookupMutex:
      		p := pprof.Lookup("mutex")
      		err = p.WriteTo(w, 2)
      	}
      	return err
      }
      
      func init() {
      	runtime.SetMutexProfileFraction(1)
      	runtime.SetBlockProfileRate(1)
      }
      
      
    3. trace

      详细的使用方式/指标之类还是看书吧,字太多。
      package main
      
      // --go run .cmd	racemain.go 2> trace.out
      
      // go build .cmd	racemain.go
      //  .main.exe
      // go tool trace trace.dat
      
      import (
      	"context"
      	"fmt"
      	"os"
      	"runtime"
      	"runtime/trace"
      	"sync"
      )
      
      func main() {
      	// 为了看协程抢占,这里设置了一个cpu 跑
      	runtime.GOMAXPROCS(1)
      
      	f, _ := os.Create("trace.dat")
      	defer f.Close()
      
      	_ = trace.Start(f)
      	defer trace.Stop()
      
      	ctx, task := trace.NewTask(context.Background(), "sumTask")
      	defer task.End()
      
      	var wg sync.WaitGroup
      	wg.Add(10)
      	for i := 0; i < 10; i++ {
      		// 启动10个协程,只是做一个累加运算
      		go func(region string) {
      			defer wg.Done()
      
      			// 标记region
      			trace.WithRegion(ctx, region, func() {
      				var sum, k int64
      				for ; k < 1000000000; k++ {
      					sum += k
      				}
      				fmt.Println(region, sum)
      			})
      		}(fmt.Sprintf("region_%02d", i))
      	}
      	wg.Wait()
      }
      
      
    4. godebug

      这个同样太长不写了。环境变量可以通过vscode写入launch.json文件中,比如

        "env": {
                  "GODEBUG":"scheddetail=1,schedtrace=1000"
              } 
      
    5. 进程诊断工具 gops

      go get -u github.com/google/gops 
      
      package main
      
      import (
      	"log"
      	"net/http"
      
      	"github.com/google/gops/agent"
      )
      
      func main() {
      	if err := agent.Listen(agent.Options{}); err != nil {
      		log.Fatal("agent listen err : %v", err)
      	}
      	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
      		_, _ = w.Write([]byte("golang projecct"))
      	})
      	_ = http.ListenAndServe(":6060", http.DefaultServeMux)
      }
      
      

      gops help查看命令,也是有很多,不细写

    6. metrics 使用expvar标准库

      code中自定义了类型,并且封装成了gin中间件,可以和gin联动了
      package main
      
      import (
      	"expvar"
      	_ "expvar"
      	"fmt"
      	"net/http"
      	"runtime"
      	"time"
      
      	"github.com/gin-gonic/gin"
      )
      
      //http://localhost:6060/debug/vars
      func main() {
      	router := NewRouter()
      	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
      		appleCounter.Add(1)
      		_, _ = w.Write([]byte("go project"))
      	})
      
      	_ = http.ListenAndServe(":6060", router)
      }
      
      var (
      	appleCounter      *expvar.Int
      	GOMAXPROCSMetrics *expvar.Int
      	upTimeMetrice     *upTimeVar
      )
      
      type upTimeVar struct {
      	value time.Time
      }
      
      func (v *upTimeVar) Set(date time.Time) {
      	v.value = date
      }
      
      func (v *upTimeVar) Add(duration time.Duration) {
      	v.value = v.value.Add(duration)
      }
      
      func (v *upTimeVar) String() string {
      	return v.value.Format(time.UnixDate)
      }
      
      func init() {
      	upTimeMetrice = &upTimeVar{value: time.Now().Local()}
      	expvar.Publish("uptime", upTimeMetrice)
      	appleCounter = expvar.NewInt("apple")
      	GOMAXPROCSMetrics = expvar.NewInt("GOMAXPROCS")
      	GOMAXPROCSMetrics.Set(int64(runtime.NumCPU()))
      }
      
      func Expvar(c *gin.Context) {
      	c.Writer.Header().Set("content-type", "application/json; charset=utf-8")
      	first := true
      	report := func(key string, value interface{}) {
      		if !first {
      			fmt.Fprintf(c.Writer, ",
      ")
      		}
      		first = false
      		if str, ok := value.(string); ok {
      			fmt.Fprintf(c.Writer, "%q: %q", key, str)
      		} else {
      			fmt.Fprintf(c.Writer, "%q: %v", key, value)
      		}
      	}
      
      	fmt.Fprintf(c.Writer, "{
      ")
      	expvar.Do(func(kv expvar.KeyValue) {
      		report(kv.Key, kv.Value)
      	})
      	fmt.Fprintf(c.Writer, "
      }
      ")
      }
      
      func NewRouter() *gin.Engine {
      	r := gin.New()
      	r.Use(gin.Logger())
      	r.Use(gin.Recovery())
      	r.GET("/debug/vars", Expvar)
      	return r
      }
      
    1. Pronmetheus

      Pronmetheus还是很出名的。四大指标类型Counter累计指标,Histogram一定时间范围内采样,Gauge可任意变化的指标,Summary也是一定时间内采样,他有仨指标,分位数分布/样本值大小总和/样本总数。

      go get -u github.com/prometheus/client_golang
      
      package main
      
      import (
      	"net/http"
      
      	"github.com/prometheus/client_golang/prometheus/promhttp"
      )
      
      func main() {
      	http.Handle("/metrics", promhttp.Handler())
      	http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
      		_, _ = w.Write([]byte("go project"))
      	})
      	_ = http.ListenAndServe(":6060", http.DefaultServeMux)
      }
      
      

      启动后访问 :6060/metrics

    2. 其他 包括附录

      1. 逃逸分析,有很多情况会造成逃逸,这个还是要经验的,初学者的我还是用命令最直接。
      // 用-gcflags查看逃逸分析过程
      go build -gcflags '-m -l' main.go
      
      // 反编译命令查看
      go tool compile -S main.go
      
      1. Go modules
        去年刚开始学时用过gopath,那玩意儿能坑死,还是modules适合老子。
      // go get后的模块会缓存在gopath/pkg/mod 和gopath/pkg/sumdb中,如果需要清理可以执行
      go clean -modcache
      
      1. 为什么defer才能recover

      panic结构是一个链表,defer结构中包含了一个对panic结构的引用,在gopanic(interface{})方法中,会触发defer,如果没有defer则会直接跳出,就不会进行接来下的recover了。

      还有一些defer了也无法recover的方法,比如fatalthrow,fatalpanic等,比如并发写入map时就会引起fatalthrow。

      10种panic方法:数组切片越界,空指针调用,过早关闭HTTP响应体(resp.body.calose()),除零,向关闭的chan发送消息,重复关闭chan,关闭未初始化的的chan,使用未初始化的map,跨goroutine处理panic,sync计数负数。

      1. 让golang更适应docker
      // 这个库可以根据cgroup的挂载信息来修改GOMAXPROCS核数
      import _ "go.uber.org/automaxprocs"
      

    完!

  • 相关阅读:
    JavaScript自定义事件
    用Java构建一个简单的WebSocket聊天室
    PHP实现支付宝小程序用户授权的工具类
    jq ajax超时设置
    gulp使用笔记
    vue学习—组件的定义注册
    echarts设置线条粗细
    求js数组的最大值和最小值
    js删除数组中的 "NaN"
    jq方法(end)
  • 原文地址:https://www.cnblogs.com/suzu/p/13907251.html
Copyright © 2020-2023  润新知