Golang的pprof性能分析

news2024/11/16 21:56:41

文章目录

    • 一、pprof 概述
    • 二、服务开启pprof
      • 1、代码中引用pprof
      • 2、服务开启一个端口,用来监听pprof
    • 三、使用pprof采集CPU耗时
      • 1、调用流程图
      • 2、查看火焰图
    • 四、使用pprof分析内存泄漏问题
      • 查看当前程序的内存占用
      • 查看goroutine的运行时间
    • 五、性能优化案例
      • 背景
      • 1、压测工具介绍
        • go-wrk
        • jmeter
      • 2、pprof分析火焰图
        • 主要问题如下
      • 3、第一波优化
        • OSS签名
        • json序列化优化
        • 优化之后的效果
      • 第二波优化
        • 优化点
        • 优化效果
        • 存在的问题
      • 第三波优化
        • 优化sql,增大连接池
        • 优化json编码器
          • json.Marshal
          • 使用json.NewEncoder
        • 减少不必要的日志打印
        • 手动设置GOGC阈值
        • 优化后的效果
          • 压测结果
          • 结论
    • 后续优化方向
      • 1、接入缓存
      • 2、限制查询数量
      • 3、更换高性能日志库
      • 4、GC调优

一、pprof 概述

      pprof 是用于可视化和分析性能分析数据的工具。但凡是Gopher应该都听过pprof的大名,内存泄漏找它,性能瓶颈找它,查看火焰图找它等等…

      本文会介绍基础的pprof使用以及性能分析和优化实例。从实际的例子出发去使用和认识pprof

如果对trace分析有兴趣,可以查看Golang的trace性能分析

二、服务开启pprof

      pprof的使用方式有两种,一种是需要在代码中调用特定API去采集并写入到文件的方式,使用的包是:

runtime/pprof

参考:你不知道的 Go 之 pprof
https://darjun.github.io/2021/06/09/youdontknowgo/pprof/

一种是基于http服务的采集,使用的包是:

net/http/pprof

我们本次就是基于http服务来使用pprof

1、代码中引用pprof

1、代码中引用pprof
_ "net/http/pprof"

2、服务开启一个端口,用来监听pprof
// Start a HTTP server to expose pprof data
	pprofAddr := "0.0.0.0:6060"
	go func(addr string) {
		if err := http.ListenAndServe(addr, nil); err != http.ErrServerClosed {
			logger.Fatalf("Pprof server ListenAndServe: %v", err)
		}
	}(pprofAddr)
	logger.Infof("HTTP Pprof start at %s", pprofAddr)

3、浏览器查看
http://服务ip:6060/debug/pprof

2、服务开启一个端口,用来监听pprof

// Start a HTTP server to expose pprof data
	pprofAddr := "0.0.0.0:6060"
	go func(addr string) {
		if err := http.ListenAndServe(addr, nil); err != http.ErrServerClosed {
			logger.Fatalf("Pprof server ListenAndServe: %v", err)
		}
	}(pprofAddr)
	logger.Infof("HTTP Pprof start at %s", pprofAddr)

3、浏览器查看
http://服务ip:6060/debug/pprof

web界面如下
在这里插入图片描述

三、使用pprof采集CPU耗时

(1) 引入Pprof
(2) 监听6060端口
(3) 采样,例如采样cpu,采样30s
	http://10.91.2.111:6060/debug/pprof/profile?seconds=30&sample_index=0&top=20
参数结束:
	seconds: 采集30s
	top: 采集耗时前20的程序
在30s内访问接口即可。30s后会下载文件到本地

(4) 分析生成的profile文件:本地开启8080端口,通过浏览器查看分析
go tool pprof -http=localhost:8080 profile(文件名)
(5) Could not execute dot; may need to install graphviz.
安装图像处理工具:
brew cleanup
brew update
brew install graphviz

1、调用流程图

在这里插入图片描述

主要指标,查看各个调用的耗时占比,以及调用链路的整体耗时。

2、查看火焰图

点击左上角的view,选择flame graph即可
在这里插入图片描述

主要指标

1、火焰图的格子越宽,代表耗时越久
2、火焰图的高度越高代表调用链路越长。
追求:宽度适中,高度适中。优化格子过宽的,或者预期之外耗时较久的。

四、使用pprof分析内存泄漏问题

查看当前程序的内存占用

参考:使用 pprof 排查 Golang 内存泄露

1、执行命令
 go tool pprof -inuse_space http://127.0.0.1:6060/debug/pprof/heap

2、进入命令行
	top: 查看当前占用内存的函数排行
	list 函数名: 查看内存占用的函数内部代码

top示例如下:
Type: inuse_space
Time: Jun 16, 2023 at 3:29pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 2706.84kB, 100% of 2706.84kB total
Showing top 10 nodes out of 32
      flat  flat%   sum%        cum   cum%
  650.62kB 24.04% 24.04%   650.62kB 24.04%  github.com/valyala/fasthttp/stackless.NewFunc
     515kB 19.03% 43.06%      515kB 19.03%  vendor/golang.org/x/net/http2/hpack.init
  514.63kB 19.01% 62.07%   514.63kB 19.01%  math/rand.newSource (inline)
  514.38kB 19.00% 81.08%   514.38kB 19.00%  google.golang.org/protobuf/reflect/protoregistry.(*Types).register
  512.20kB 18.92%   100%   512.20kB 18.92%  runtime.malg
         0     0%   100%   514.63kB 19.01%  github.com/nacos-group/nacos-sdk-go/v2/clients.NewConfigClient
         0     0%   100%   514.63kB 19.01%  github.com/nacos-group/nacos-sdk-go/v2/clients/config_client.NewConfigClient
         0     0%   100%   514.63kB 19.01%  github.com/nacos-group/nacos-sdk-go/v2/clients/config_client.NewConfigProxy
         0     0%   100%   514.63kB 19.01%  github.com/nacos-group/nacos-sdk-go/v2/common/nacos_server.NewNacosServer
         0     0%   100%   514.63kB 19.01%  github.com/spf13/cobra.(*Command).Execute

3、通过查看当前服务的内存占用,可以分析内存泄漏等

查看goroutine的运行时间

在这里插入图片描述

      看到长时间运行的goroutine就需要小心了,如果是非系统的goroutine,有可能是业务上的goroutine一直没释放,这也是内存泄漏的来源之一。

五、性能优化案例

背景

      Go服务进行批量查询的时候,由于返回的数据中包含大量的OSS签名数据,导致实际的QPS很低,且延时比较高,因此决定针对这个接口做性能分析及优化。

查询的大致流程是:

1、参数校验
2、从数据库批量查询
3、OSS签名,处理数据
4、json序列化返回

      流程很简单,就是普通的查询接口,但是当查询的数量比较多的时候,比如查询30多条记录,QPS表现实在是不尽如人意。压测结果如下:
在这里插入图片描述

本次性能分析及优化暂时不涉及到数据库缓存,由于时间原因,只做除数据库外的其他优化。

1、压测工具介绍

go-wrk

      go-wrk 是一个用于测试 Go 语言网络服务器性能的命令行工具。它基于 Go 的标准库 net/http 包,使用 goroutine 实现了高并发请求。

go-wrk [flags] url:启动测试,并指定要测试的 URL。
-c value:指定并发数。默认为 50。
-d value: 压测时间,单位是秒
-t value:指定请求超时时间。默认为 20 秒。
-H "header:value":设置 HTTP 请求头。
-v:显示详细输出。
-m: 设置 HTTP 请求方法,例如 GET, POST, PUT, DELETE 等常用的请求方法。


例如:
go-wrk -c 100 -d 30 -T 2000 -H "Content-Type: application/json, Authorization: Bearer token" \n
http://localhost:8080/test -m POST -body '{"name":"bob","age":30}'

返回:
15841 requests in 38.848392606s, 117.13MB read
Requests/sec:           407.76
Transfer/sec:           3.01MB
Avg Req Time:           2.452395215s
Fastest Request:        66.236651ms
Slowest Request:        19.945042378s
Number of Errors:       232

适合本地使用,安装方便。主要关注的参数是请求数。

jmeter

      jmeter是一款开源的、用于功能测试和性能测试的Java应用程序。安装的时候需要界面来配置自动化场景,目前是使用meterSphere中自带的jmeter,主要关注TPS指标。

      本来想在Linux下安装个命令行压测一下,实测结果是安装复杂,但是功能很全面。公司测试使用的是jmeter,那我们后续的压测都以jmeter的结果为准

2、pprof分析火焰图

在这里插入图片描述

主要问题如下

oss签名占比将近40%
GC耗时比较高,占比>10%
go-restful写入response占比>20%,本质上是json序列化

3、第一波优化

OSS签名

      由于签名设置过期时间为1个小时,且写入OSS的数据不频繁更新,因此可以考虑加缓存。
这里使用轻量级go-cacheOSS签名缓存并不需要考虑分布式缓存,go-cache足够了。

go get github.com/patrickmn/go-cache

var c = cache.New(50*time.Minute, 10*time.Minute) //设置过期队列
c.Set("key", value, cache.DefaultExpiration) // 写入缓存
cachedValue, found := c.Get("key") // 读出缓存

json序列化优化

      原生json库使用反射,性能上可能差点,我们使用高性能的json-iterator/go库来进行json序列化。

Go常用包(三十三):高性能json解析器
在这里插入图片描述

// 使用很简单,安装之后开箱即用
var fastJson = jsoniter.ConfigFastest
jsonData, _ := fastJson.Marshal(data)

优化之后的效果

在这里插入图片描述

1、oss签名占比<1%
2、批量查询sql耗时较多,主要集中在scanAll部分,这部分的耗时跟数据库查询字段的多少有关
3、json序列化耗时较多,但比之前的原声json要好一些

第二波优化

优化点

1、减少json解析字段,按需返回。
2、sql优化,返回字段缩减。
3、由于不分页,因此可以减少count查询的sql

优化效果

在这里插入图片描述

存在的问题

1、数据库占用37%
2、json耗时从27%下降到17%
3、gc占用过多,4G内存,只能用到100M就GC了
4、日志打印耗时10%
5、gvalid校验参数耗时占比6.6%

第三波优化

优化sql,增大连接池

unsafeDb.SetMaxOpenConns(200) // 设置最大连接数为 200
unsafeDb.SetMaxIdleConns(10)  // 设置最大空闲连接数为 10

优化json编码器

// 原来先解析成[]byte,后赋值给resp
// 新增了变量且多了赋值操作
jsonData, _ := fastJson.Marshal(common.SuccessRESTResp(data))
resp.Header().Set(restful.HEADER_ContentType, "application/json")
resp.WriteHeader(200)
resp.Write(jsonData)



// 直接把go-restful的resp *restful.Response 作为参数
// 编码器会直接把编码结果写入到resp,省去了原来的拷贝赋值
enc := fastJson.NewEncoder(resp)
enc.Encode(common.SuccessRESTResp(data))

实测json序列化效果并没有提升。

json.Marshal

在这里插入图片描述

使用json.NewEncoder

在这里插入图片描述

      理论上来说,把写入response合并到一起,减少了数据拷贝才对,但是实测发现json耗时从27%33%。 查看详情发现,主要是多了切片动态扩容的部分,如下所示:
在这里插入图片描述

减少不必要的日志打印

logrusinfo部分转为debug。不必要的infof打印参数去掉或者转为debugf

手动设置GOGC阈值

      目前使用内存太少了,分配了4G只使用了几十M。Go服务的GC阈值可以通过GOGC这个环境变量来配置,默认是100,也就是说:
GC的部分可以参考:Golang的trace性能分析

当前内存10M,那么下次GC是在10M + 10M * 100% = 20M的时候进行GC。

设置阈值增大10倍,预计在内存占比达到100-200M区间才会进行GC
在这里插入图片描述

优化后的效果

在这里插入图片描述

压测结果

在这里插入图片描述

结论
1、数据库占比31%
2、日志占比下降,目前占比4%
3、json序列化占比20%左右
4、主要耗时还是在sql,需要上缓存
5、参数校验耗时低于1%
6、GC占比略有下降,查看trace发现GC频率明显下降。

后续优化方向

1、接入缓存

      高QPS必定离不开缓存,数据库能支撑的QPS有限。使用redis做缓存是合适的。

2、限制查询数量

      json序列化随着查询参数的增多,占比越来越大。适当减少数据的返回,对json序列化提升会很大。

3、更换高性能日志库

日志的重要性不需要强调,因为性能而放弃日志也是不可取的。作为现在统计和排查问题的关键,我们可以选择使用更高性能的日志库。
uber开源的高性能日志库在这里插入图片描述
实话实说,logrus的性能实在是差。

4、GC调优

      Go 1.9之前只能设置GOGC方法来调整GC的阈值,1.9之后可以直接设置一个MemoryLimit,达到limitGC,不达到就不GC
      问题是,GC阈值变的很高就真的很好吗,一次GC扫描的内存变大,标记耗时也会延长,实际的效果可能并不能如意,需要慎重和多次验证找到最佳GC阈值。

end

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

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

相关文章

WIFI中的频段、信道、信道带宽

一、波长、波速与频率 波长波速/频率 “波速”由“介质”决定。 “频率”由“波源”决定。 “波长”由“介质”(波速V)、“波源”(频率f)共同决定。&#xff08;λV/f&#xff09; 波长&#xff08;wavelength&#xff09;&#xff1a; 指波在一个振动周期内传播的距离。也就…

【正点原子STM32连载】 第三十二章 光敏传感器实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第三十…

【网络面试必问】浏览器如何委托协议栈完成消息的收发

接上一篇&#xff1a;【面试中的网络知识】DNS原理-如何实现域名和IP地址的查询转换  在前面的博客中&#xff0c;提到过无数次&#xff0c;浏览器作为应用程序&#xff0c;本身是不具备向网络中发送网络请求的能力&#xff0c;要委托操作系统的内核协议栈来完成。协议栈再调用…

新手学习Vmp之控制流程图生成

新手学习Vmp之控制流程图生成 控制流程图的生成对于反混淆分析来说是非常重要的一步&#xff0c;这里记录一下我研究的过程&#xff0c;以Vmp2为例子。 这里我的环境准备如下: Visual Studio IDA SDK Capstone Unicorn Graphviz IDA SDK插件环境&#xff0c;主要是有一些AP…

1.2数据机构——算法和复杂度

一、算法 1、概念&#xff1a;算法是对特定问题求解的一种描述&#xff08;或步骤&#xff09;&#xff0c;是指令的特定序列 2、程序数据结构算法 3、算法的特性&#xff1a; 有穷性&#xff1a;算法是有穷的&#xff0c;程序是无穷的 确定性&#xff1a;每条指令有确定的…

一文搞懂什么是Raid

RAID 1. 基本概念2. RAID 03. RAID 14. RAID 015. RAID 56. RAID6 1. 基本概念 RAID&#xff08;Redundant Array of Independent Disks&#xff09;是一种磁盘阵列技术&#xff0c;通过将多个物理磁盘组合成一个大容量的逻辑磁盘&#xff0c;提高磁盘存储的性能和可靠性。 R…

TOGAF 标准对数字化企业支持

这篇来点高大尚的&#xff0c;对技术、产品管理者和架构师写方案应该有用&#xff0c;其它不多谢&#xff0c;直接转入正题。 一、概述 TOGAF标准是在需要购买服务器硬件和网络设备的时候开始发展的&#xff1b;需要规划数据中心空间、电源和冷却&#xff0c;并协商和购买产品…

微服务SpringCloudday1 认识微服务与服务注册(Eureka与nacos)

SpringCloud01 1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff…

arduino w801 流水灯

先复制个图过来 来源 https://www.cnblogs.com/milton/p/15621540.html 前两天老外跟联盛德网站回复我arduino库有完善的了 GitHub - board707/w80x_arduino: w806 for arduino ide package arduino 添加库地址 https://raw.githubusercontent.com/board707/w80x_arduino/ha…

【genius_platform软件平台开发】第九十八讲:嵌入式网络接口(MAC、PHY)

1. 嵌入式网络简介 1.1 嵌入式下的网络硬件接口 提起网络&#xff0c;我们一般想到的硬件就是“网卡”&#xff0c;现在网卡已经是通过一个芯片来完成了&#xff0c;嵌入式网络硬件分为两部分&#xff1a;MAC和PHY&#xff0c;大家都是通过看数据手册来判断一款SOC是否支持网络…

Linux tar.xz 格式的文件正确的解压命令

Linux tar.xz 最近下载 Linux kernel&#xff0c;好像最近流行 tar.xz 格式的后缀 对于 xz 后缀的压缩文件&#xff0c;我之前的解压方式是分为两步&#xff1a; xz -d xxx.tar.xz 解压成 xxx.tar 格式文件&#xff0c;然后再 tar xf xxx.tar 解压文件。 这样的操作不仅比较的…

【Java基础学习打卡11】Path环境变量的配置

目录 前言一、为什么配置环境变量二、如何配置环境变量三、JDK11的环境变量配置总结 前言 本文我们要知道为什么配置环境变量&#xff0c;自己思考不配置环境变量可以吗&#xff1f;JDK 11 如何配置环境变量。 一、为什么配置环境变量 原因很简单&#xff0c;就是方便命令的查…

痛点-调研-明确需求-实现-测试-发布 不需要手一步到位使用AGI生成去广告脚本,复制粘贴发布到Greasy Fork

总算又想起密码了, 自从用了语雀后, 其他平台基本都不再使用了 csdn 真的**, c h a t g p t 是禁词(已经改为了AGI, 通用型人工智能), 你倒是说清楚啊,直接来一句违反社区规定, 莫名其妙, 得靠猜…服了 今天来补上一篇利用AGI生成的js去广告脚本 前置知识(不看也问题不大) …

MyBatis junit 日志框架logback

JUnit是专门做单元测试的组件 <!-- junit依赖 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency>…

牛客网最全的 Java 面试八股文大合集汇总

就目前大环境来看&#xff0c;跳槽成功的难度比往年高很多。一个明显的感受&#xff1a;今年的面试&#xff0c;无论一面还是二面&#xff0c;都很考验 Java 程序员的技术功底。这不马上又到了面试跳槽的黄金段&#xff0c;成功升职加薪&#xff0c;不成功饱受打击。当然也要注…

关于laravel使用Elastic Search的一些记录

文章目录 1. 准备工作2. 本地安装elastic search3. laravel安装es依赖4. laravel中使用es参考链接 1. 准备工作 因为我本地php版本是7.3.4&#xff0c;不支持太高的es。 所以使用如下环境: laravel6 php7.3.4 elastic search 7.17.2 2. 本地安装elastic search 1. 下载安装包…

备战金九银十,两份 JAVA 面试题 2023 最新整合版,祝你脱颖而出

前言 马上又准备到了一年一度的金九银十环节&#xff0c;作为一年中的跳槽求职高峰期&#xff0c;相信有很多朋友都已经开始着手准备面试了&#xff0c;但是网上的面试题杂七杂八的&#xff0c;所以今天分享 2 份整合好的合集版&#xff0c;从基础到深入比较全面。即适合初入社…

windows10系统-14-Hexo博客框架和DevSidecar加速访问GitHub

HexoGitee零代码基础从0到1部署博客全流程&#xff08;一&#xff09; HexoGithub博客搭建教程 亲测解决npm ERR! Unexpected end of JSON input while parsing near…的方法 Hexo-修改Hexo主题 hexo免费主题 现在市面上的博客很多&#xff0c;如CSDN&#xff0c;博客园&#x…

【干货】Android系统定制基础篇:第二部分

1、Android Launcher3支持键盘切换焦点 Android Launcher3 默认并不支持键盘操作&#xff0c;无法切换焦点&#xff0c;在一些需要支持键盘或遥控操作的设备中无法使用&#xff0c;因些对 Launcher3 做简单修改&#xff0c;使其支持键盘切换焦点。 diff --git a/packages/app…

C语言学习笔记:指针

✨博文作者&#xff1a;烟雨孤舟 &#x1f496; 喜欢的可以 点赞 收藏 关注哦~~ ✍️ 作者简介: 一个热爱大数据的学习者 ✍️ 笔记简介&#xff1a;作为大数据爱好者&#xff0c;以下是个人总结的学习笔记&#xff0c;如有错误&#xff0c;请多多指教&#xff01; 目录 简介 …