Go性能分析工具

news2024/11/28 2:28:29

前言

作为后端研发,性能分析是我们在研发过程中必然会会遇到的环节,接口耗时、堆栈溢出、内存泄露等等。所谓工欲善其事必先利其器,之前在java中我们是使用arthas这一大神器,不得不说确实好用,想了解arthas的可以看下我之前的文章:Arthas性能分析工具。本节内容我们带来Go的分析神器pprof的使用,帮助我们分析各种性能问题。

pprof介绍

pprof是GoLang程序性能分析工具,和arthas这种工具不一样,pprof是内置在Go里面的,我们只需要开启对应的功能就可以进行采集,本质上还是生成对应的性能数据文件,然后通过pprof打开。不过pprof还提供了web形式的访问,这对于server程序而言真实太方便了,通过http的交互方式,我们就可以进入性能分析的界面。

开启pprof

pprof可以支持单机程序和server项目,单机程序的话我们需要在程序里面硬编码,通过的打点的方式告诉pprof采集CPU、内存等文件,然后文件会落入指定的路径最终进行分析。但是我们实际开发场景大都是server项目,所以这里我也以server项目来举例说明。

1、Go原生的http服务框架

当采用go原生的http服务框架,使用pprof非常的简单:只需要导入net/http/pprof包即可。代码如下:

package main
 
import (
    "fmt"
    "net/http"
    _ "net/http/pprof"
)
 
func HelloWorld(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}
 
func main() {
    http.HandleFunc("/", HelloWorld)
 
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        fmt.Println(err)
    }
}

2、使用gin框架实现的服务

这个也是我们常用的一种方式,Gin框架如果要添加pprof, 可以借助github.com/gin-contrib/pprof包,并且在项目启动的入口位置注册一下pprof,代码如下:

package routers
 
import (
    "github.com/gin-contrib/pprof"
    "github.com/gin-contrib/pprof"  //引入gin对应的pprof包
    "github.com/gin-gonic/gin"
    "go-server/app/controller"
)
 
func init() {
 
    //创建一个gin示例
    router := gin.Default()
 
    pprof.Register(router)  //注册pprof
 
    //注册路径
    user := router.Group("/performance")
    {
        user.GET("/pertest", controller.CollectPerformance)
    }
 
    //监听端口
    router.Run(":8080")
}

server类型的项目,启动之后出现如下日志说明注册pprof成功:

浏览器中访问:localhost:8080/debug/pprof

 

各项指标说明:

pprof分析 

性能分析

性能分析主要是分析程序的耗时情况,这个在pprof是采集CPU的信息,在CPU的采集信息里会有每个函数占用CPU片段的时间,这个也间接的反应了函数的耗时时长。

通过上面这些步骤开启pprof之后,接下来我们就要进行数据的采集,pprof并不是默认采集数据的,而是需要手动去采集一段时间的性能数据(默认是30s),所以开启这个不需要担心会对系统造成负担,只要不主动采集,pprof是不会进行工作。

开启CPU采集通过访问url地址:http://<host>:<port>/debug/pprof/profile,可以带参数?seconds = 60表示采集60s,访问这个地址之后,程序会block住等待一定的时间,在这个时间内我们进行的操作就会被采集进去,一般采用交互式命令的方式开启采集:

go tool pprof http://<host>:<port>/debug/pprof/profile

开启采集之后我们就可以立即访问需要测试的程序,然后采集数据,在本示例中我通过一个接口来模拟数据:

访问http://localhost:8080/performance/pertest地址会调用CollectPerformance这个函数,在这个函数中调用了另外一个耗时函数:

func CollectPerformance(ctx *gin.Context) {
    performanceWait()  //这是一个耗时函数
}
 
func performanceWait() {
    for i := 2000000; i >= 0; i-- {
        fmt.Println(i)
    }
}

访问程序之后,pprof的交互界面经过默认的采集时间之后就可以进行查看数据了,下面就是分析阶段了:

1、top n 按照函数耗时排序,展示前多少个

这里重点看cum这个值,这个值表示的是函数本身执行的时间和调用其他函数的时间,我们可以直接按照cum来排序,top 20 -cum

各项指标说明:

 

2、list

list命令有点类似于arthas中的trace,可以列出程序的具体的耗时信息,比如上面我看到performanceWait这个函数耗时较长,那么通过list去看一下:

上面看到在20行消耗的cpu时间片最长,因为这里有一个很大的循环

注意:

  • pprof采集的数据是这段时间的总和,比如上面的1.35s表示这段时间程序执行时间的总和,不是平均值
  • 上述时间是函数CPU的时间,不一定是函数真实的运行时间,比如在函数里有一行time.sleep(13 * time.Second),这个时间并不会计算进去

 

 

内存分析

内存分析这里我们主要是分析堆内存,也是我们平时程序作用的地方。pprof提供了堆内存的检测方式,可以通过实施快照的方式帮助我们dump内存数据并且进行分析。

dump堆内存:go tool pprof http://localhost:6060/debug/pprof/heap

访问上面的地址pprof就会dump出当时的堆内存情况,这个和cpu的prfile不一样,不是执行完就block住等待数据产生,而是直接dump出执行时间点的堆内存信息

 我在接口中编写一个不断申请内存的程序,导致最终内存溢出,通过文件进行分析。

func CollectHeap(ctx *gin.Context) {
    tick := time.Tick(time.Second)
    var buf []byte
    stime := time.Now()
    for range tick {
        // 1秒1M内存
        buf = append(buf, make([]byte, 1024*1024)...)
 
        fmt.Printf("%f\n", time.Now().Sub(stime).Seconds())
    }
}

访问接口让程序执行,然后通过heap快照内存后进行分析,分析的方式和cpu一样,也是top和list命令:

执行top可以看到CollectHeap这个函数占用内存很高,通过list可以定位到具体的哪一行

再测试一个大对象占用的场景,在函数里定义一个map,循环往里面放数据。

package controller
 
import (
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/google/uuid"
    "go-server/app/models"
    "time"
)
 
var userMap = map[string]models.User{}
 
func CollectHeap(ctx *gin.Context) {
    for i := 1000000; i >= 0; i-- {
        uuid := uuid.New().String()
        user := models.User{
            Name: uuid,
            Age:  i,
        }
        userMap[uuid] = user
    }
}

访问接口执行程序,然后看下堆内存情况

通过top可以看到在CollectHeap函数中有内存占用比较大

通过list可以看到在map赋值这一行占用内存最多,这里有一点和Java不太一样,就是pprof的分析都是基于函数为基础,所有所有的都显示到函数执行上,比如Java的内存分析会直接分析到对象,某个具体的map,但是Go里面还是对应函数执行,会有一点点不直观

 goroutine分析

分析goroutine主要是分析阻塞这种情况,比如系统中存在大量的goroutine没有执行完,导致泄露

代码示例:

func CollectGoroutine(ctx *gin.Context) {
    outCh := make(chan int) //无缓冲channel
    stime := time.Now()
    // 每1s个goroutine
    for {
        time.Sleep(1 * time.Second)
        go alloc(outCh)
        fmt.Printf("last: %dseconds\n", int(time.Now().Sub(stime).Seconds()))
    }
}
 
// alloc分配1M内存,goroutine会阻塞,不释放内存,导致泄漏
func alloc(outCh chan<- int) {
    buf := make([]byte, 1024*1024*1)
    _ = len(buf)
    fmt.Println("alloc make buffer done")
 
    outCh <- 0
    fmt.Println("alloc finished")
}

访问接口会不断开启goroutine分配内存,并且系统中的goutinue数量会不断上升,具体分析如下:

pprof分析goroutine推荐使用web界面访问的方式,这种访问也是实时的,每次访问都是实时的快照系统中的gotoutine情况,有两种分析方式:

1、goroutine总览:http://localhost:8080/debug/pprof/goroutine?debug=1

在goroutine总览里面可以看到系统中存在的goroutine数量,通过实时刷新我们可以看到这个数值在直线上升,说明肯定有阻塞的地方,导致goroutine没有释放,通过第二种方式查看明细

1、goroutine明细:http://localhost:8080/debug/pprof/goroutine?debug=2

明细里面会展示所有状态的goroutine情况

running状态:

 

sleep状态:

我们在代码41行执行了sleep的方法

block状态:

这里可以看到有很多线程都是阻塞在chan的发送上面,耗时几分钟,这里也列了代码中阻塞在哪一行

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

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

相关文章

Zookeeper单机模式搭建

1、下载 ​wget https://dlcdn.apache.org/zookeeper/zookeeper-3.6.3/apache-zookeeper-3.6.3-bin.tar.gz 2、解压 tar -zxvf apache-zookeeper-3.6.3-bin.tar.gz 3、进入 apache-zookeeper-3.6.3-bin目录下&#xff0c;创建data cd apache-zookeeper-3.6.3-bin mkdir da…

面试官:性能测试瓶颈调优你是真的会吗?

引言&#xff1a;性能瓶颈调优 在实际的性能测试中&#xff0c;会遇到各种各样的问题&#xff0c;比如 TPS 压不上去等&#xff0c;导致这种现象的原因有很多&#xff0c;测试人员应配合开发人员进行分析&#xff0c;尽快找出瓶颈所在。 理想的性能测试指标结果可能不是很高&…

CGAL的2D符合规定的三角剖分和网格

1、符合规定的三角剖分 1.1、定义 如果三角形的任何面的外接圆在其内部不包含顶点&#xff0c;则该三角形是 Delaunay 三角形。 约束 Delaunay 三角形是一种尽可能接近 Delaunay 的约束三角形。 约束 Delaunay 三角形的任何面的外接圆在其内部不包含从该面可见的数据点。 如果…

Leetcode—1768.交替合并字符串【简单】

2023每日刷题&#xff08;五十五&#xff09; Leetcode—1768.交替合并字符串 实现代码 class Solution { public:string mergeAlternately(string word1, string word2) {int len1 word1.size(), len2 word2.size();string ans;for(int i 0; i < len1 || i < len2;…

编译Android14 AOSP原生代码并运行X86模拟器镜像过程记录

最近在研究Android Entreprise部分的特性&#xff0c;需要在Android手机上分析WorkProfile相关的源码&#xff0c;因为新买的Pixel样机还未到货&#xff0c;看了几天Android源码&#xff0c;迫切需要上真机对比分析。 又听说最近几年Android模拟器已经有些进步&#xff0c;至少…

IDEA远程调试与JDWP调试端口RCE漏洞

文章目录 前言Docker远程调试Java调试原理远程调试实践 JDWP端口RCE调试端口探测调试端口利用 总结 前言 在对一些 Java CVE 漏洞的调试分析过程中&#xff0c;少不了需要搭建漏洞环境的场景&#xff0c;但是本地 IDEA 搭建的话既麻烦&#xff08;通过 pom.xml 导入各种漏洞组…

散列卡片悬停变为整齐列表

效果展示 CSS 知识点 transform 属性运用 页面整体布局 <ul><li><div class"box"><img src"./user1.jpg" /><div class"content"><h4>Hamidah</h4><p>commented on your photo.<br />…

每日OJ题_算法_双指针④_力扣11. 盛最多水的容器

目录 力扣11. 盛最多水的容器 解析代码 力扣11. 盛最多水的容器 11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; 难度 中等 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两…

APP备案(Android) - 获取签名证书公钥、MD5

因为近期刚针对各应用平台对APP备案时间节点要求进行了统一整理&#xff0c;然后隔天就被要求提供一下app相关的的公钥和MD5&#xff0c;虽然很快就解决了这个事情&#xff0c;但忍不住又稍微衍生了一下&#xff0c;但行小步&#xff0c;莫问远方吧 关联Blog APP备案(Android)…

【Spring Boot 源码学习】ApplicationListener 详解

Spring Boot 源码学习系列 ApplicationListener 详解 引言往期内容主要内容1. 初识 ApplicationListener2. 加载 ApplicationListener3. 响应应用程序事件 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们从 Spring Boot 的启动类 SpringApplication 上入手&am…

harmonyOS创建低的代码开发模式项目 带你基本不写代码完成一个界面跳转的小案例

之前 我们讲了 JavaScript类Web开发模式和ArkTS开发模式 但是 有人就会说 我一点代码基础都没有 难道就不能开发鸿蒙了吗&#xff1f; 其实也是可以的 本文来讲述一下低代码开发模式 我们先打开编辑器 先创建一个项目 默认模板 直接下一步 这里配置中 我们输入名称 然后选择…

【Vulnhub 靶场】【hacksudo: ProximaCentauri】【简单 - 中等】【20210608】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hacksudo-proximacentauri,709/ 靶场下载&#xff1a;https://download.vulnhub.com/hacksudo/hacksudo-ProximaCentauri.zip 靶场难度&#xff1a;简单 - 中等 发布日期&#xff1a;2021年06月08日 文件大小&…

龙迅LT9721 MIPIDSI/CSI/HDMI桥接到TYPE-C/DP 支持高达4K30HZ的分辨率

Lontium LT9721 LT9721描述&#xff1a; Lontium LT9721是MIPI/HDMI到DP转换器&#xff0c;内部有C型替代模式开关和PD控制器。 对于MIPI DSI输入&#xff0c;LT9721具有一个单端口MIPI DSI接收器&#xff0c;具有1个时钟通道和4个数据通道&#xff0c;每个数据通道的最大运行频…

Linux基础指令详解(1)

操作系统的概念 百度百科 操作系统&#xff08;英语&#xff1a;Operating System&#xff0c;缩写&#xff1a;OS&#xff09;是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。根据运行的环境&#xff0c;操作系…

【git】关于git二三事

文章目录 前言一、创建版本库1.通过命令 git init 把这个目录变成git可以管理的仓库2.将修改的内容添加到版本库2.1 git add .2.2 git commit -m "Xxxx"2.3 git status 2.4 git diff readme.txt3.版本回退3.1 git log3.2 git reset --hard HEAD^ 二、理解工作区与暂存…

西南科技大学C++程序设计实验十(函数模板与类模板)

一、实验目的 1. 掌握函数模板与类模板; 2. 掌握数组类、链表类等线性群体数据类型定义与使用; 二、实验任务 1. 分析完善以下程序,理解模板类的使用: (1)补充类模板声明语句。 (2)创建不同类型的类对象,使用时明确其数据类型? _template<typename T>__…

使用linux CentOS本地部署SQL Server数据库

&#x1f308;个人主页&#xff1a;聆风吟 &#x1f525;系列专栏&#xff1a;数据结构、Cpolar杂谈 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 &#x1f4cb;前言一. 安装sql server二. 局域网测试连接三. 安装cpolar内网穿透四. 将sqlserver映射…

Grafana系列-Loki-基于日志实现告警

系列文章 Loki 系列文章 前言 实际应用中除了基于 Metrics 告警, 往往还有基于日志的告警需求, 可以作为基于 Metrics 告警之外的一个补充. 典型如基于 NGINX 日志的错误率告警.本文将介绍如何基于 Loki 实现基于日志的告警. 本文我们基于以下 2 类实际场景进行实战演练: …

零基础一看就会?Python实现性能自动化测试竟然如此简单

一、思考❓❔ 1.什么是性能自动化测试? 性能 系统负载能力超负荷运行下的稳定性系统瓶颈自动化测试 使用程序代替手工提升测试效率性能自动化 使用代码模拟大批量用户让用户并发请求多页面多用户并发请求采集参数&#xff0c;统计系统负载能力生成报告 2.Python中的性能自动化…

汽车网络安全--关于UN R155认证的思考

1.UN R155概述 2020年6月25日,联合国颁布了全球首个汽车网络安全强制性法规 -- UN 155,详细规定了关于评估网络安全措施的审核条款、制造商和供应商降低网络安全风险的方法以及实施风险评估的义务等。 法规适用于与信息安全相关的M类(4轮及以上载客汽车)、N类(四轮载货汽车)…