Heim > Artikel > Backend-Entwicklung > was ist go pprof
pprof ist das Leistungsanalysetool von Go. Während des laufenden Prozesses des Programms kann es die laufenden Informationen des Programms aufzeichnen, z. B. CPU-Auslastung, Speichernutzung, Goroutine-Laufstatus usw. Wenn eine Leistungsoptimierung oder Fehlersuche erforderlich ist , diese Aufzeichnungen Die Informationen sind sehr wichtig. Es gibt viele Möglichkeiten, pprof zu verwenden. Go hat bereits ein „net/http/pprof“ gepackt. Mit ein paar einfachen Befehlszeilen können Sie pprof öffnen, laufende Informationen aufzeichnen und Webdienste bereitstellen.
Die Betriebsumgebung dieses Tutorials: Windows 7-System, GO Version 1.18, Dell G3-Computer.
Profil wird im Allgemeinen als Leistungsanalyse bezeichnet. Die Übersetzung im Wörterbuch lautet „Profil“ (Substantiv) oder „Profil“ (Verb) und beschreibt ... Bei einem Computerprogramm handelt es sich bei seinem Profil um verschiedene allgemeine Informationen über ein Programm, wenn es ausgeführt wird, einschließlich CPU-Belegung, Speicher, Thread-Status, Thread-Blockierung usw. Wenn Sie diese Informationen über das Programm kennen, können Sie Probleme und Fehlerursachen im Programm leicht lokalisieren.
pprof ist das Leistungsanalysetool von Go. Während des laufenden Prozesses des Programms kann es die laufenden Informationen des Programms aufzeichnen, z. B. CPU-Auslastung, Speichernutzung, Goroutine-Laufstatus usw. Wenn eine Leistungsoptimierung oder Fehlersuche erforderlich ist , diese Aufzeichnungen Die Informationen sind sehr wichtig.
golang bietet eine relativ gute Unterstützung für die Profilerstellung. Die Standardbibliothek bietet die Profilbibliotheken „runtime/pprof“ und „net/http/pprof“ und bietet außerdem viele nützliche visuelle Tools, die Entwickler bei der Profilerstellung unterstützen.
Für Online-Dienste und einen HTTP-Server greifen Sie auf die von pprof bereitgestellte HTTP-Schnittstelle zu, um Leistungsdaten zu erhalten. Tatsächlich ruft die unterste Ebene hier natürlich auch die von runtime/pprof bereitgestellte Funktion auf, die in eine Schnittstelle gekapselt ist, um externen Netzwerkzugriff bereitzustellen. In diesem Artikel wird hauptsächlich die Verwendung von „net/http/pprof“ vorgestellt.
Es gibt viele Möglichkeiten, pprof zu verwenden: net/http/pprof. Mit ein paar einfachen Befehlszeilen können Sie pprof öffnen, laufende Informationen aufzeichnen und bereitstellen Mit dem Webdienst können laufende Daten über den Browser und die Befehlszeile abgerufen werden.
Wie man die Überwachung in Webdiensten aktiviert, schauen wir uns ein einfaches Beispiel an.
package main import ( "fmt" "net/http" _ "net/http/pprof" ) func main() { // 开启pprof,监听请求 ip := "0.0.0.0:8080" if err := http.ListenAndServe(ip, nil); err != nil { fmt.Printf("start pprof failed on %s\n", ip) } dosomething() }
Importieren Sie das Paket „net/http/pprof“ und öffnen Sie den Überwachungsport. Zu diesem Zeitpunkt können Sie das Profil des Programms abrufen. In der tatsächlichen Produktion kapseln wir diese Funktion im Allgemeinen in eine Goroutine. Wie überprüfen Sie es also nach dem Öffnen? Es gibt drei Möglichkeiten:
Öffnen Sie einen Browser, geben Sie ip:port/debug/pprof ein und drücken Sie die Eingabetaste.
pprof liefert viele Leistungsdaten. Die spezifische Bedeutung ist:
allocs Es handelt sich um die Speicherzuteilung aller Objekte, und Heap ist die Speicherzuweisung aktiver Objekte, die später ausführlich beschrieben wird.
1. Wenn die CPU-Leistungsanalyse aktiviert ist, pausiert die Go-Laufzeit alle 10 ms, um den Aufrufstapel und die zugehörigen Daten der aktuell ausgeführten Goroutine aufzuzeichnen. Nachdem die Leistungsanalysedaten auf der Festplatte gespeichert wurden, können wir die Hotspots im Code analysieren.
2. Bei der Speicherleistungsanalyse wird der Aufrufstapel aufgezeichnet, wenn der Heap zugewiesen wird. Standardmäßig wird alle 1000 Zuweisungen eine Stichprobe durchgeführt. Dieser Wert kann geändert werden. Die Stapelzuweisung wird nicht durch die Speicheranalyse erfasst, da sie jederzeit freigegeben wird. Weil es sich bei der Speicheranalyse um eine Stichprobenmethode handelt und weil sie die Speicherzuweisung und nicht die Speichernutzung aufzeichnet. Daher ist es schwierig, Tools zur Speicherleistungsanalyse zu verwenden, um die spezifische Speichernutzung eines Programms genau zu bestimmen.
3. Die Blockierungsanalyse ist eine sehr einzigartige Analyse. Sie ähnelt in gewisser Weise der CPU-Leistungsanalyse, zeichnet jedoch die Zeit auf, die Goroutine damit verbringt, auf Ressourcen zu warten. Die Blockierungsanalyse ist sehr hilfreich bei der Analyse von Engpässen bei der Programmparallelität. Die Blockierungsleistungsanalyse kann zeigen, wenn eine große Anzahl von Goroutinen blockiert ist. Die Blocking-Performance-Analyse ist ein spezielles Analysetool und sollte nicht zur Analyse verwendet werden, bevor CPU- und Speicherengpässe beseitigt wurden.
Wenn Sie auf einen Link klicken, werden Sie natürlich feststellen, dass die Lesbarkeit schlecht ist und es fast unmöglich ist, sie zu analysieren. Wie im Bild gezeigt:
Klicken Sie auf den Heap und scrollen Sie nach unten. Sie können einige interessante Daten sehen, die für die Fehlerbehebung hilfreich sein können, aber im Allgemeinen nicht verwendet werden.
heap profile: 3190: 77516056 [54762: 612664248] @ heap/1048576 1: 29081600 [1: 29081600] @ 0x89368e 0x894cd9 0x8a5a9d 0x8a9b7c 0x8af578 0x8b4441 0x8b4c6d 0x8b8504 0x8b2bc3 0x45b1c1 # 0x89368d github.com/syndtr/goleveldb/leveldb/memdb.(*DB).Put+0x59d # 0x894cd8 xxxxx/storage/internal/memtable.(*MemTable).Set+0x88 # 0x8a5a9c xxxxx/storage.(*snapshotter).AppendCommitLog+0x1cc # 0x8a9b7b xxxxx/storage.(*store).Update+0x26b # 0x8af577 xxxxx/config.(*config).Update+0xa7 # 0x8b4440 xxxxx/naming.(*naming).update+0x120 # 0x8b4c6c xxxxx/naming.(*naming).instanceTimeout+0x27c # 0x8b8503 xxxxx/naming.(*naming).(xxxxx/naming.instanceTimeout)-fm+0x63 ...... # runtime.MemStats # Alloc = 2463648064 # TotalAlloc = 31707239480 # Sys = 4831318840 # Lookups = 2690464 # Mallocs = 274619648 # Frees = 262711312 # HeapAlloc = 2463648064 # HeapSys = 3877830656 # HeapIdle = 854990848 # HeapInuse = 3022839808 # HeapReleased = 0 # HeapObjects = 11908336 # Stack = 655949824 / 655949824 # MSpan = 63329432 / 72040448 # MCache = 38400 / 49152 # BuckHashSys = 1706593 # GCSys = 170819584 # OtherSys = 52922583 # NextGC = 3570699312 # PauseNs = [1052815 217503 208124 233034 ......] # NumGC = 31 # DebugGC = false
除了浏览器,Go还提供了命令行的方式,能够获取以上信息,这种方式用起来更方便。
使用命令go tool pprof url
可以获取指定的profile文件,此命令会发起http请求,然后下载数据到本地,之后进入交互式模式,就像gdb一样,可以使用命令查看运行信息,以下为使用示例:
# 下载cpu profile,默认从当前开始收集30s的cpu使用情况,需要等待30s go tool pprof http://localhost:8080/debug/pprof/profile # 30-second CPU profile go tool pprof http://localhost:8080/debug/pprof/profile?seconds=120 # wait 120s # 下载heap profile go tool pprof http://localhost:8080/debug/pprof/heap # heap profile # 下载goroutine profile go tool pprof http://localhost:8080/debug/pprof/goroutine # goroutine profile # 下载block profile go tool pprof http://localhost:8080/debug/pprof/block # goroutine blocking profile # 下载mutex profile go tool pprof http://localhost:8080/debug/pprof/mutex
接下来用一个例子来说明最常用的四个命令:web、top、list、traces
。
接下来以内存分析举例,cpu和goroutine等分析同理,读者可以自行举一反三。
首先,我们通过命令go tool pprof url
获取指定的profile/heap文件,随后自动进入命令行。如图:
第一步,我们首先输入web
命令,这时浏览器会弹出各个函数之间的调用图,以及内存的之间的关系。如图:
这个图的具体读法,可参照:中文文档 或者英文文档 这里不多赘述。只需要了解越红越大的方块,有问题的可能性就越大,代表可能占用了更多的内存,如果在cpu的图中代表消耗了更多cpu资源,以此类推。
接下来 top、list、traces
三步走可以看出很多想要的结果。
top 按指标大小列出前10个函数,比如内存是按内存占用多少,CPU是按执行时间多少。
top会列出5个统计数据:
cum%: 是累计量占总量的百分比。
这样我们可以看到到底是具体哪些函数占用了多少内存。
当然top后也可以接参数,top n
可以列出前n个函数。
list可以查看某个函数的代码,以及该函数每行代码的指标信息,如果函数名不明确,会进行模糊匹配,比如list main
会列出main.main
和runtime.main
。现在list sendToASR
试一下。
可以看到切片中增加元素时,占用了很多内存,左右2个数据分别是flat和cum。
traces 打印所有调用栈,以及调用栈的指标信息。使用方式为traces+函数名
(模糊匹配)。
在命令行之中,还有一个重要的参数 -base
,假设我们已经通过命令行得到profile1与profile2,使用go tool pprof -base profile1 profile2
,便可以以profile1为基础,得出profile2在profile1之上出现了哪些变化。通过两个时间切片的比较,我们可以清晰的了解到,两个时间节点之中发生的变化,方便我们定位问题(很重要!!!!)
打开可视化界面的方式为:go tool pprof -http=:1234 http://localhost:8080/debug/pprof/heap
其中1234是我们指定的端口
Top
该视图与前面所讲解的 top 子命令的作用和含义是一样的,因此不再赘述。
Graph
为函数调用图,不在赘述.
Peek
Im Vergleich zur Draufsicht fügt diese Ansicht die Anzeige von Kontextinformationen hinzu, d. h. den Ausgabeaufrufer/Aufgerufenen der Funktion.
Quelle
Diese Ansicht fügt hauptsächlich quellcodeorientierte Verfolgung und Analyse hinzu, und Sie können sehen, wo der Overhead hauptsächlich verbraucht wird.
Flammendiagramm
entspricht dem Flammendiagramm des Ressourcenverbrauchs. Das Auslesen des Flammendiagramms wird hier nicht beschrieben.
Das zweite Dropdown-Menü ist wie in der Abbildung dargestellt:
alloc_objects,alloc_space
表示应用程序分配过的资源,不管有没有释放,inuse_objects,inuse_space
gibt die regelmäßige Zuweisung von Ressourcen der Anwendung an, die noch nicht freigegeben wurden. Name: Bedeutung
Gesamtbetrag zugewiesener Speicher (unabhängig von der Freigabe) | |
---|---|
第一个下拉菜单可以与第二个下拉菜单相组合,可以查看临时变量的火焰图,常驻变量的内存调用图等。 tips:
操作方法上面我们已经看完了go pprof 的所有操作,接下来讲解一下go tool pprof 的具体使用流程。
具体案例案例一:goroutine泄漏启动程序后,用浏览器方式打开profile: 发现内存持续上升,同时goroutine也在持续上升,初步判断,内存泄漏是由于goroutine泄漏导致的。 接下来通过命令行方式抓取goroutine的情况:命令行输入: 分析的流程一、使用web命令查看调用图,大概了解目前的goroutine的泄露情况: 通过观察,最引入注目的便是runtime.gopark这个函数,这个函数在所有goroutine泄漏时都会出现,并且是大头,接下来我们研究一下这个函数的作用: func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) { mp := acquirem() gp := mp.curg status := readgstatus(gp) mp.waitlock = lock mp.waitunlockf = unlockf gp.waitreason = reason mp.waittraceev = traceEv mp.waittraceskip = traceskip releasem(mp) mcall(park_m) } 该函数的作用为: 1、调用 2、调用 3、调用 综上:该函数的关键作用就是将当前的 goroutine 放入等待状态,这意味着 goroutine 被暂时被搁置了,也就是被运行时调度器暂停了。 此外,我们发现有两个函数的goroutine的达到了67,很可疑,在我们接下来的验证中要格外留意。 二、使用top命令,获取更加具体的函数信息: 与上面分析的结论相似,我们要将关注点放在三个开启了67个goroutine的函数。 三、 四、使用list+函数名,查看具体代码的问题。 通过list命令我们可以清楚的看出问题代码是堵塞在哪里。接下来的篇幅我们来分析一下这个问题: goroutine泄漏知识什么是goroutine泄漏:如果你启动了一个 goroutine,但并没有符合预期的退出,直到程序结束,此goroutine才退出,这种情况就是 goroutine 泄露。当 goroutine 泄露发生时,该 goroutine 的栈(一般 2k 内存空间起)一直被占用不能释放,goroutine 里的函数在堆上申请的空间也不能被 垃圾回收器 回收。这样,在程序运行期间,内存占用持续升高,可用内存越来也少,最终将导致系统崩溃。
select底层也是channel实现的,如果所有case上的操作阻塞,select内部的channel便会阻塞,goroutine也无法继续执行。所以我们使用channel时一定要格外小心。 通过分析上面两幅图的情况,可以判断是因为select在所有case上死锁了,再深入代码分析,是因为项目中的的语音模型并发能力弱,在语音发包速度快起来的时候无法处理,导致select不满足条件,导致goroutine泄漏,应该在for循环之外加一个
案例二:内存泄漏我们通过grafana发现内存出现泄漏: 这一次我们不使用命令行,而是使用图形化界面来定位问题。 发现内存占用很有可能是 而调用 func readAll(r io.Reader, capacity int64) (b []byte, err error) { buf := bytes.NewBuffer(make([]byte, 0, capacity)) defer func() { e := recover() if e == nil { return } if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge { err = panicErr } else { panic(e) } }() _, err = buf.ReadFrom(r) return buf.Bytes(), err } // bytes.MinRead = 512 func ReadAll(r io.Reader) ([]byte, error) { return readAll(r, bytes.MinRead) } 可以看到 func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) { b.lastRead = opInvalid // If buffer is empty, reset to recover space. if b.off >= len(b.buf) { b.Truncate(0) } for { if free := cap(b.buf) - len(b.buf); free < MinRead { // not enough space at end newBuf := b.buf if b.off+free < MinRead { // not enough space using beginning of buffer; // double buffer capacity newBuf = makeSlice(2*cap(b.buf) + MinRead) } copy(newBuf, b.buf[b.off:]) b.buf = newBuf[:len(b.buf)-b.off] b.off = 0 } m, e := r.Read(b.buf[len(b.buf):cap(b.buf)]) b.buf = b.buf[0 : len(b.buf)+m] n += int64(m) if e == io.EOF { break } if e != nil { return n, e } } return n, nil // err is EOF, so return nil explicitly }
GC算法的触发时机 golang的GC算法为三色算法,按理说会回收临时变量,但是触发GC的时机导致了这个问题:
解决方法 //req.AduioPack,err=ioutil.ReadAll(c.Resquest.Body) buffer:=bytes.NewBuffer(make[]byte,0,6400) _,err:=io.Copy(buffer,c.Resquest.Body) temp:=buffer.Bytes() req.AduioPack=temp 不是一次性把文件读入内存,而是申请固定的内存大小。 【Verwandte Empfehlungen: Go-Video-Tutorial, Programmierunterricht】 |
Das obige ist der detaillierte Inhalt vonwas ist go pprof. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!