go 使用pprof分析性能,trace追踪

news2024/12/22 22:00:35

前言

pprof是Go程序自带的性能分析工具,prof是profile(画像)的缩写,用于分析go程序的性能。

 Profile Descriptions:

  • allocs:

     A sampling of all past memory allocations 已分配内存采样
  • block:

     Stack traces that led to blocking on synchronization primitives 导致同步原函数阻塞的堆栈追踪
  • cmdline:

     The command line invocation of the current program 当前程序的命令调用列表
  • goroutine:

     Stack traces of all current goroutines 当前所有goroutines的堆栈追踪
  • heap:

     A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample. 存活的对象内存分配采样。你可以指定gc get参数在获取heap样本前运行gc。
  • mutex:

     Stack traces of holders of contended mutexes 互斥锁持有着的堆栈追踪
  • profile:

     CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile. CPU配置文件。你可以在秒为单位的get参数指定周期,获取配置文件后,使用go tool pprof指令分析配置文件。
  • threadcreate:

     Stack traces that led to the creation of new OS threads 导致操作系统线程创建的堆栈追踪
  • trace:

     A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace. 对当前程序的执行情况进行跟踪。你可以在秒为单位的get参数指定周期。获取配置文件后,使用go tool pprof指令分析配置文件。这个非常重要,可以使用trace查看每个栈信息的执行时间,查看耗时情况分析。

使用分析一般常用CPU,内存,线程栈阻塞分析,还有trace。

pprof

pprof实际上就是包引用即可生效,即执行init函数

_ "runtime/pprof"

因为init函数定义了http的pprof的url

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)
}

pprof demo

所以如果开启了http的能力(包括第三方http框架),那么只需要引入包,即可实现pprof能力。

如果没有http呢,此时就需要手动读取信息,写入文件才行,官方demo代码

    //获取cpu profile
   cpuFile, _ := os.OpenFile("cpu.prof", os.O_CREATE|os.O_RDWR, 0644)
   defer cpuFile.Close()
   pprof.StartCPUProfile(cpuFile)
   defer pprof.StopCPUProfile()
   //获取内存profile
   memFile, _ := os.OpenFile("mem.prof", os.O_CREATE|os.O_RDWR, 0644)
   defer memFile.Close()
   pprof.WriteHeapProfile(memFile)

运行后即可写入文件,可根据实际情况编写代码

如果是http,那么就简单很多了,这里以默认官方http为例,第三方框架有封装,可以根据框架的实际加入pprof。

import (
	"fmt"
	"net/http"
	_ "net/http/pprof" #这个包引入即可,发现低版本的go需要手动引入pprof init函数的路由规则,高版本一般不需要,如果是低版本,可以把init函数的代码复制过来
)

func helloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hello world!")
	w.Write([]byte("haha......"))
}

func main() {
	http.HandleFunc("/", helloWorld)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Printf("ListenAndServe: %s", err)
	}
}

pprof分析 

访问http://localhost:8080/debug/pprof/ 

 

 可以直接点击各个链接即可查看,其中full goroutine stack dump

其中这个类似java的jstack,可以看到所有的线程栈。

数据分析通过命令  go tool pprof 进行,可以对文件,即上面demo写的prof文件分析,也可以使用http读取方式实时分析,固定文件方式,可以通过

go tool pprof cpu.prof(xxx.prof)

这种用法适合文件离线分析,一般而言推荐在线分析,因为http在线分析就会生成文件

go tool pprof http://localhost:8080/debug/pprof/profile 

此时实际上就连上 /debug/pprof/profile 这个url了,那么分析内容

 实际上各个功能都有说明,英文也很简单

部分功能需要安装特殊的插件才行,MacOS安装如下,其他平台类似,比如yum或者apt等

重构代码,调用看看top线程 占用

func HelloWorld(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hello world!")
	for i := 0; i < 100000; i++ {
		go sayHello(w)
	}
}

func sayHello(w http.ResponseWriter) {
	fmt.Println("haha.........")
}

func main() {
	http.HandleFunc("/hi", HelloWorld)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Printf("ListenAndServe: %s", err)
	}
}

执行后,通过top 数字,可以看到占用CPU的栈,这里要注意,如果一个函数调用时间较短就可能会看不见,可以多调用几次函数,保证被采样,也可以通过?seconds=xxx设置采样时间。

也可以使用list,查看函数执行的占用

输入web,可以看到svg的cpu信息

 使用go tool pprof -http=":8085" http://localhost:8080/debug/pprof/profile (端口自定义)

 

 访问如下url:

可以看到top信息

 

火焰图

实际上上面的信息已经足够分析,不过火焰图是一种常规展示方式,方便最直观的分析,实际上火焰图就在ui图表中

 打开火焰图,可以直观的看到demo的示例方法调用时间,占用等信息

 

trace使用

trace是一个非常重要的功能(也是pprof的一部分,但是需要代码埋点),用于函数和方法的追踪,实际上笔者在前几篇的文章中使用了K8S的封装trace,实际上这个是一个道理,但是内置go的sdk,不需要依赖。

官方示例代码

func main() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
  ......
}

改造上面的示例demo

func sayHello(w http.ResponseWriter) {
	r := trace.StartRegion(context.Background(), "HelloWorld")
	defer r.End()
	fmt.Println("haha.........")
}

func main() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
	http.HandleFunc("/hi", HelloWorld)
	ctx, task := trace.NewTask(context.Background(), "HelloWorld")
	defer task.End()
	err := http.ListenAndServe(":8080", nil)
	trace.Log(ctx, "http", "http started!")
	if err != nil {
		fmt.Printf("ListenAndServe: %s", err)
	}

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	fmt.Println("end-----------------")
}

启动应用,然后执行函数,发现

 go tool trace trace.out 

出现解析报错

 

 实际上是trace的stop没执行,trace必须执行stop才能形成完整的trace

继续改造

func main() {
	f, _ := os.Create("trace.out")
	defer f.Close()
	trace.Start(f)
	defer trace.Stop()
	http.HandleFunc("/hi", HelloWorld)
	ctx, task := trace.NewTask(context.Background(), "HelloWorld")
	defer task.End()
	go http.ListenAndServe(":8080", nil)
	trace.Log(ctx, "http", "http started!")

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	fmt.Println("end-----------------")
}

必须执行stop才行,所以,在demo中需要把进程杀掉 ,实际上也可以自定义stop的地方

然后执行go tool trace trace.out,自动跳出页面,可以看trace埋点,发现函数执行时间,栈的各个函数执行过程

箭头图示就是埋点内容,当然还有其他功能

 

 

还发现了trace2,不知道怎么用

查看源代码,使用方法见代码注释

/*
Package trace implements tracing of requests and long-lived objects.
It exports HTTP interfaces on /debug/requests and /debug/events.

A trace.Trace provides tracing for short-lived objects, usually requests.
A request handler might be implemented like this:

	func fooHandler(w http.ResponseWriter, req *http.Request) {
		tr := trace.New("mypkg.Foo", req.URL.Path)
		defer tr.Finish()
		...
		tr.LazyPrintf("some event %q happened", str)
		...
		if err := somethingImportant(); err != nil {
			tr.LazyPrintf("somethingImportant failed: %v", err)
			tr.SetError()
		}
	}

The /debug/requests HTTP endpoint organizes the traces by family,
errors, and duration.  It also provides histogram of request duration
for each family.

A trace.EventLog provides tracing for long-lived objects, such as RPC
connections.

	// A Fetcher fetches URL paths for a single domain.
	type Fetcher struct {
		domain string
		events trace.EventLog
	}

	func NewFetcher(domain string) *Fetcher {
		return &Fetcher{
			domain,
			trace.NewEventLog("mypkg.Fetcher", domain),
		}
	}

	func (f *Fetcher) Fetch(path string) (string, error) {
		resp, err := http.Get("http://" + f.domain + "/" + path)
		if err != nil {
			f.events.Errorf("Get(%q) = %v", path, err)
			return "", err
		}
		f.events.Printf("Get(%q) = %s", path, resp.Status)
		...
	}

	func (f *Fetcher) Close() error {
		f.events.Finish()
		return nil
	}

The /debug/events HTTP endpoint organizes the event logs by family and
by time since the last error.  The expanded view displays recent log
entries and the log's call stack.
*/
package trace // import "golang.org/x/net/trace"

试了一下,跟业务强耦合

func HelloWorld(w http.ResponseWriter, r *http.Request) {
	//trace2.Traces(w, r)
	t := trace2.New("http", "HelloWorld")
	defer t.Finish()
	fmt.Fprintf(w, "hello world!")
	for i := 0; i < 100000; i++ {
		go sayHello(w)
	}
}

func sayHello(w http.ResponseWriter) {
	fmt.Println("haha.........")
}

func main() {

	http.HandleFunc("/hi", HelloWorld)
	go http.ListenAndServe(":8080", nil)
	trace2.NewEventLog("http", "http-started!")

	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	<-signalChan
	fmt.Println("end-----------------")
}

 执行结果,没有trace的能力强大

 

 

总结

go语言跟Java类似实际上内置了很多强大的性能分析能力,可以获取go程序执行的方方面面,难道是因为都是gc语言,笔者查询pprof的原理说是gc的stw(stop the world)的过程中处理的,Java实际上有jstack jmap等自带的命令,go直接集成到sdk了,坏处是代码侵入,但是可以精确定位go函数执行的过程,配合webui,尤其是火焰图和trace,基本上大部分问题都可以解决。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/493565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

梯度下降算法

梯度下降算法的公式&#xff1a; 公式依次代表为“下次迭代的点”、“目前所在的点”、“学习率”和“最大负向梯度”。按照这个公式&#xff0c;每次迭代都会根据上次的步长减去学习率乘以“梯度”的值&#xff0c;去一步一步更新&#xff0c;这样能可以每次迭代都朝着最大负方…

基于xml的Spring应用-1

基于xml的Spring应用 Spring的get方法 方法定义返回值和参数Object getBean (String beanName)根据beanName从容器中获取Bean实例&#xff0c;要求容器中Bean唯一返回值为Object&#xff0c;需要强转T getBean (Class type)根据Class类型从容器中获取Bean实例&#xff0c;要求…

SpringBoot 创建 WebService

开发环境: IDEA 2022.1.4 目录 1. 概述 2. 实现步骤 2.1 POM现加依赖 2.2 定义接口 IWebService 2.3 创建类 IWebServiceImpl 并实现接口 IWebService 2.4 配置类 CxfConfig 2.5 启动服务 2.6 测试 1. 概述 虽然webservice这块使用很少&#xff0c;但在局域网作服务还是相…

自动化测试之 selenium 的安装以及 selenium IDE 录制自动化脚本的用法

文章目录 1. 什么是自动化测试1&#xff09;单元测试2&#xff09;接口自动化3&#xff09;UI 自动化 2. Selenium 介绍1&#xff09;Selenium IDE2&#xff09;Webdriver3&#xff09;Selenium Grid 3. 如何使用 Selenium IDE 录制脚本4. 在 python 环境中安装 Selenium 框架 …

RSA--维纳攻击--代码和题目分析

文章目录 维纳攻击原理&#xff1a;维纳攻击脚本[羊城杯 2020]RRRRRRRSA 1题目描述&#xff1a;题目分析&#xff1a; 收获与体会&#xff1a; 维纳攻击原理&#xff1a; 两位大佬讲得非常清楚&#xff08;搬运工就是我&#xff09;&#xff1a;https://zhuanlan.zhihu.com/p/…

代码随想录算法训练营第五十二天| 300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组

文章目录 300.最长递增子序列674. 最长连续递增序列718. 最长重复子数组 300.最长递增子序列 想清楚如何推导dp数组是关键 两层for循环&#xff0c;因为递增序列不是连续的 题目链接&#xff1a;代码随想录 解题思路&#xff1a; 1.dp[i]表示i之前包括i的以nums[i]结尾的最长递…

SpringBoot整合Mybatis-Plus实现浏览数据新增、Redis进行热度排名

在开发Web项目时&#xff0c;常用到的技术就是SpringBoot和Mybatis-Plus。本文将介绍如何使用SpringBoot整合Mybatis-Plus实现一个浏览数据新增功能&#xff0c;以及如何用Redis进行热度排名统计&#xff0c;最后用Vue进行数据渲染。 一、SpringBoot整合Mybatis-Plus 1. 新建…

0 基础 Java 自学之路(2023年最新版)

目录 一、语言的选择二、Java学习路线三、学习资料哪里找&#xff1f;1、Java经典书籍推荐2、Java经典视频推荐3、经典博客推荐 四、如何规划时间&#xff1f;五、是否要报培训班&#xff1f; 写了10多年的代码&#xff0c;之前做过某东的高级架构师&#xff0c;在技术这条路上…

BIOS主板(非UEFI)安装fedora36的方法

BIOS主板(非UEFI)安装fedora36的方法 现实困难&#xff1a;将Fedora-Workstation-Live-x86_64-38-1.6.iso写入U盘制作成可启动U盘启动fedora38安装时报错如下内容&#xff1a; Failed to find a suitable stage1 device: EFI System Partition cannot be of type ntfs: EFI Sys…

携创教育:自考、成考、开放大学几年能够毕业拿证?

目前&#xff0c;国家承认的成人学历提升的形式只有3种&#xff0c;分别是自考&#xff0c;成考&#xff0c;开放大学。 ▼各学历形式拿证时间▼ ★自学考试 自考没有入学考试&#xff0c;只需要参加相应的课程考试&#xff0c;所有课程考试合格后&#xff0c;符合毕业条件即可…

【论文简述】Cross-Attentional Flow Transformer for Robust Optical Flow(CVPR 2022)

一、论文简述 1. 第一作者&#xff1a;Xiuchao Sui、Shaohua Li 2. 发表年份&#xff1a;2021 3. 发表期刊&#xff1a;arxiv 4. 关键词&#xff1a;光流、Transformer、自注意力、交叉注意力、相关体 5. 探索动机&#xff1a;由于卷积的局部性和刚性权重&#xff0c;有限…

Flutter 中使用 Widgetbook 管理你的组件

Flutter 中使用 Widgetbook 管理你的组件 前言 Flutter 界面开发中我们有几个痛点 &#xff1a; 与设计师协作复用一套设计规范&#xff08;figma&#xff09; 可视化的管理你的组件代码&#xff08;基础组件、业务组件&#xff09; 不同设备尺寸测试你的组件 实时修改你的测试…

【事务】在spring中事务不生效的场景总结

在开发过程中会遇到事务失效的问题&#xff0c;所以在开发中要特别注意&#xff0c;下面我自己总结了事务不生效的场景&#xff0c;提醒自己。 一般出现问题分为几大类&#xff1a; 配置问题spring aop代理问题底层数据库不支持事务问题Transactional 配置错误开发过程中使用错…

Go有序map:orderedmap

有序映射 与传统的无序映射&#xff08;Map&#xff09;不同&#xff0c;orderedmap包中的有序映射&#xff08;OrderedMap&#xff09;可以记录键值对的插入顺序。orderedmap提供了一些有用的API&#xff0c;用来存储、删除、查询和遍历键值对。 获取OrderedMap 你可以通过Ord…

地面分割--Fast Segmentation of 3D Point Clouds for Ground Vehicles论文阅读与源码分析

文章目录 1写在前面的话2点云投影分块3地面点云分割4核心代码阅读投影分块直线拟合代码分割地面点云 5实验效果参考 1写在前面的话 这篇文章属于地面分割领域非常经典的一篇论文&#xff0c;论文具有速度快&#xff0c;在一定程度能适应有坡度的地形&#xff0c;文章主要分为两…

学习使用ansible自动化运维工具

目录 一、虚拟机环境 二、yum方式部署 三、ansible使用 &#xff08;一&#xff09;将ansible服务器上文件分发给各节点 1. 创建一个要复制的文件&#xff0c;并复制到Ansible管理主机上 2.编辑Ansible的playbook文件&#xff0c;将copy模块添加到任务列表中 3. 运行play…

【c++迭代器模拟实现】

目录&#xff1a; 前言一、STL初始二、六大组件之迭代器迭代器初始迭代器的模拟实现&#xff08;1&#xff09;victor正向迭代器反向迭代器1反向迭代器2反向迭代器3 &#xff08;2&#xff09;list正向迭代器反向迭代器 总结 前言 打怪升级&#xff1a;第52天 一、STL初始 什…

和chatgpt一样的大模型LLaMA可以运行在pc上?

未来已来,大模型依据压缩模型的方式,可以在普通的PC上运行. LLaMA Facebook的LLaMA 模型和Georgi Gerganov 的llama.cpp的结合。 LLaMA&#xff0c;这是一组包含 7B 到 65B 参数的基础语言模型。我们在数万亿个令牌上训练我们的模型&#xff0c;并表明可以仅使用公开可用的数…

【Android入门到项目实战-- 9.1】—— 传感器的使用教程

目录 传感器的定义 三大类型传感器 1、运动传感器 2、环境传感器 3、位置传感器 传感器开发框架 1、SensorManager 2、Sensor 3、SensorEvent 4、SensorEventListener 一、使用传感器开发步骤 1、获取传感器信息 1)、获取传感器管理器 2)、获取设备的传感器对象列…

Java红黑树

概述 红黑树是一种自平衡的二叉查找树&#xff0c;是计算机科学中用到的一种数据结构。1972年出现的&#xff0c;当时被称之为平衡二叉B树。在1978年被修改为红黑树。红黑树是一种特殊的二叉查找树&#xff0c;红黑树上的每一个节点都有存储位表示节点的颜色。每一个节点可以是…