Go 1.22 - 更加强大的 Go 执行跟踪

在这里插入图片描述

原文:Michael Knyszek - 2024.03.14

runtime/trace 包含了一款强大的工具,用于理解和排查 Go 程序。这个功能可以生成一段时间内每个 goroutine 的执行追踪。然后,你可以使用 go tool trace 命令(或者优秀的开源工具 gotraceui)来可视化和探索这些追踪数据。

追踪的魔力在于,它可以轻易地揭示出程序中那些难以通过其他方式看到的信息。例如,一个并发瓶颈可能是很多 goroutine 在同一个 channel 上阻塞,这在 CPU profile 中可能很难被看到,因为没有执行样本可以采样。但在执行追踪中,执行的缺失或不足 将以惊人的清晰度展现出来,被阻塞的 goroutine 的堆栈追踪将迅速指向问题根源。

在这里插入图片描述

Go 开发者甚至能够使用 任务、区域 和 日志 来对自己的程序进行插桩,以便将更高的关注点与底层执行细节进行关联。

问题

不幸的是,执行追踪中的丰富信息往往无法触及。历史上,四个主要问题阻碍了追踪的使用。

  • 追踪的开销很大。
  • 追踪的扩展性不强,可能会变得过大而无法分析。
  • 往往不清楚何时开始追踪以捕获特定的不良行为。
  • 鉴于缺乏用于解析和解释执行追踪的公共包,只有最具冒险精神的 gopher 才能编程分析追踪。

如果你在过去几年中使用过追踪,可能已经被这些问题中的一个或多个所困扰。但我们很高兴地分享,在过去的两个 Go 版本中,我们在这四个领域都取得了大的进步。

低开销的追踪

在 Go 1.21 之前,许多应用的追踪运行时开销大约在 10-20% 的 CPU 之间,这限制了追踪的使用情况,不能像 CPU profiling 那样持续使用。事实证明,追踪的大部分成本都归结于 traceback。运行时产生的许多事件都附带有堆栈追踪,这些对于实际识别 goroutine 在关键执行时刻的行为是非常有价值的。

感谢 Felix Geisendörfer 和 Nick Ripley 在优化 traceback 效率方面的工作,执行追踪的运行时 CPU 开销已经大幅降低,对于许多应用来说,现在只有 1-2%。你可以在 Felix 的精彩博客文章中关于这项工作的内容。

可扩展的追踪

追踪格式及其事件的设计主要考虑到了相对高效的数据生成和输出(emission),但需要工具来解析和保留整个追踪的状态。几百 MiB 的追踪可能需要几 GiB 的 RAM 来分析!

不幸的是,这个问题对于跟踪的生成方式至关重要。为了保持运行时开销低,所有事件都被写入等同于线程局部缓冲区的地方。但这意味着事件出现的顺序并非其真实顺序,追踪工具需要负责弄清楚究竟发生了什么。

使追踪在保持开销低的同时具有可扩展性的关键是,不定期地切分正在生成的追踪。每个切分点都会表现得有点像同时禁用和重新启用追踪。到目前为止的所有追踪数据都会代表一个完整且自包含的追踪,而新的追踪数据将无缝地接续前者。

你可能想象到,修复这个问题需要在运行时重新思考和重写追踪实现的很多基础部分。我们很高兴地说,这项工作在 Go 1.22 中完成,并且现在已经普遍可用。许多很好的改进随着重写一起出现,包括对 go tool trace 命令的一些改进。如果你感到好奇,所有的细节都在设计文档中。

(注意:go tool trace 仍然将完整的追踪加载到内存中,但现在已经可以移除这个限制,用于 Go 1.22+ 程序生成的追踪。)

Flight recording

假设你正在开发一个网络服务,一个 RPC 花费了很长时间。你不能在已经知道 RPC 花费了一段时间的点开始追踪,因为慢请求的根本原因已经发生并且没有被记录下来。

有一种可以帮助解决这个问题的技术,叫做 Flight recording,你可能已经在其他编程环境中熟悉了。Flight recording 的关键理念是,持续进行追踪,并始终保留最新的追踪数据,以便随时使用。然后,一旦发生了有趣的事情,程序就可以直接写出它所拥有的所有内容!

在追踪可以被切分之前,这基本上是无法实现的。但是,由于开销较低,连续跟踪现在是可行的,以及运行时现在可以在需要的时候随时切分追踪,结果,实现 Flight recording 非常简单。

因此,我们很高兴宣布一个 Flight recorder 实验特性,可在 golang.org/x/exp/trace 包中找到。

请试试看!下面是一个示例,设置了 Flight recorder 以捕获长时间的 HTTP 请求,以帮助你入门。

// 设置 Flight recorder 。
fr := trace.NewFlightRecorder()
fr.Start()

// 设置并运行一个 HTTP 服务器。
var once sync.Once
http.HandleFunc("/my-endpoint", func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()

    // 做一些事情...
    doWork(w, r)

    // 我们看到了一个长请求。拍摄一张快照!
    if time.Since(start) > 300*time.Millisecond {
        // 为了简单起见,只做一次,但你可以拍摄多于一张的快照。
        once.Do(func() {
            // 抓取快照。
            var b bytes.Buffer
            _, err = fr.WriteTo(&b)
            if err != nil {
                log.Print(err)
                return
            }
            // 将它写入一个文件。
            if err := os.WriteFile("trace.out", b.Bytes(), 0o755); err != nil {
                log.Print(err)
                return
            }
        })
    }
})
log.Fatal(http.ListenAndServe(":8080", nil))

如果你有任何反馈,无论是积极的还是消极的,都请分享到 proposal issue 中!

追踪 reader API

伴随着追踪实现的重写,我们也努力清理了其他的追踪内部工具,如 go tool trace。这促使我们尝试创建一个足够优秀,以至于可以分享的追踪 reader API,这个 API 能够让追踪数据更易于访问。

就像 Flight recorder 一样,我们很高兴宣布我们也有一个实验性的追踪 reader API,我们希望分享出来。它在与 Flight recorder 相同的包 golang.org/x/exp/trace 中可用。

我们认为它足够好以开始在其之上构建东西,所以请试试看!下面是一个示例,它测量了阻塞等待网络的 goroutine 阻塞事件的比例。

// 从 STDIN 开始读取。
r, err := trace.NewReader(os.Stdin)
if err != nil {
    log.Fatal(err)
}

var blocked int
var blockedOnNetwork int
for {
    // 读取事件。
    ev, err := r.ReadEvent()
    if err == io.EOF {
        break
    } else if err != nil {
        log.Fatal(err)
    }

    // 处理事件。
    if ev.Kind() == trace.EventStateTransition {
        st := ev.StateTransition()
        if st.Resource.Kind == trace.ResourceGoroutine {
            id := st.Resource.Goroutine()
            from, to := st.GoroutineTransition()

            // 寻找阻塞的 goroutines,并计数。
            if from.Executing() && to == trace.GoWaiting {
                blocked++
                if strings.Contains(st.Reason, "network") {
                    blockedOnNetwork++
                }
            }
        }
    }
}
// 打印我们找到的内容。
p := 100 * float64(blockedOnNetwork) / float64(blocked)
fmt.Printf("%2.3f%% instances of goroutines blocking were to block on the network\n", p)

就像 Flight recorder 一样,proposal issue 将是留下反馈的好地方!

我们要特别提一下 Dominik Honnef,他是早期的试用者,提供了很好的反馈,并且对 API 的旧版追踪提供了支持。

感谢你们!

这项工作在很大程度上得益于诊断工作组的帮助,这个工作组是一年多前由来自 Go 社区的各个利益相关者共同发起的,并向公众开放的。

我们想花一点时间感谢那些,在过去一年中经常参加诊断会议的社区成员:Felix Geisendörfer, Nick Ripley, Rhys Hiltner, Dominik Honnef, Bryan Boreham, thepudds。

你们所有人的讨论、反馈和投入的工作,对我们取得今天的成就发挥了重要作用,谢谢你们!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

漏洞发现-漏扫项目篇Poc开发Yaml语法插件一键生成匹配结果交互提取

知识点 1、Nuclei-Poc开发-环境配置&编写流程 2、Nuclei-Poc开发-Yaml语法&匹配提取 3、Nuclei-Poc开发-BurpSuite一键生成插件 章节点: 漏洞发现-Web&框架组件&中间件&APP&小程序&系统 扫描项目-综合漏扫&特征漏扫&被动漏扫…

C语言经典算法-8

文章目录 其他经典例题跳转链接41.基数排序法42.循序搜寻法(使用卫兵)43.二分搜寻法(搜寻原则的代表)44.插补搜寻法45.费氏搜寻法 其他经典例题跳转链接 C语言经典算法-1 1.汉若塔 2. 费式数列 3. 巴斯卡三角形 4. 三色棋 5. 老鼠…

Flutter开发进阶之使用Socket实现主机服务(二)

Flutter开发进阶之使用Socket实现主机服务(二) Flutter开发进阶之使用Socket实现主机服务(一) 在完成局域网内设备定位后就可以进入微服务的实操了。 I、构建Socket连接池 一、定义Socket 使用socket_io_client socket_io_client: ^2.0.3+1导入头文件 import packag…

LiveGBS流媒体平台GB/T28181功能-HTTPS 服务支持配置开启什么时候需要开启HTTPS测试SSL证书配置HTTPS测试证书

LiveGBS功能支持HTTPS 服务支持配置开启什么时候需要开启HTTPS测试SSL证书配置HTTPS测试证书 1、配置开启HTTPS1.1、准备https证书1.1.1、选择Nginx类型证书下载 1.2、配置 LiveCMS 开启 HTTPS1.2.1 web页面配置1.2.2 配置文件配置 2、HTTPS测试证书3、验证HTTPS服务4、为什么要…

图书馆管理系统 2.后台系统管理模块编写

后端 1.实体类编写 用户实体类 package jkw.pojo;import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import lombok.Data;import java.io.Serializable; import java.util.List;/*** 用户*/ Data public class …

机器学习 - 选择模型

接着这一篇博客做进一步说明: 机器学习 - 准备数据 PyTorch moduleExplaintorch.nnContains all of the building blocks for computational graphs (essentially a series of computations executed in a particular way). nn 模块为用户提供了丰富的神经网络组件…

【理解机器学习算法】之分类问题的模型评估(ROC-AUC)

ROC曲线(接收者操作特性曲线)和AUC(曲线下面积)是在不同阈值设置下,用于分类问题的性能度量工具。下面是它们所代表的含义以及使用方法: ROC曲线 代表含义:ROC曲线是一个图形化的表示&#xf…

反射 Reflection

反射 反射的概念 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到加载完类之后,在堆中就产生了一个Class类型…

SurfaceFlinger实战dump获取单个Layer图像方案学员改进成果

背景: hi,粉丝朋友们: 在马哥课程的实战实现dump单个图层的发布后,很多学员朋友就纷纷享马哥要了相关源码,相关的链接请参考这里: https://blog.csdn.net/learnframework/article/details/136323076 学员…

前端项目,个人笔记(三)【Vue-cli - api封装-axios使用举例】

目录 前言 1、axios配置与测试 1.1、配置 1.2、测试 2、使用axios案例-渲染header 3、Pinia优化重复请求 3.1、为什么? 3.2、使用Pinia优化代码步骤 步骤一:在main.js中创建 Pinia 实例,并将其作为插件添加到 Vue 应用中 步骤二&am…

【QT+QGIS跨平台编译】之八十四:【QGIS_Gui跨平台编译】—【错误处理:未实例化QgsMapLayer - QgsHighlight】

文章目录 一、未实例化QgsMapLayer二、错误处理 一、未实例化QgsMapLayer 报错信息: 二、错误处理 第31行修改为: #include "qgsmaplayer.h"

【Leetcode】2684. 矩阵中移动的最大次数

文章目录 题目思路代码结果 题目 题目链接🔗 给你一个下标从 0 开始、大小为 m x n 的矩阵 grid ,矩阵由若干 正 整数组成。 你可以从矩阵第一列中的 任一 单元格出发,按以下方式遍历 grid : 从单元格 (row, col) 可以移动到 (…

Flutter-仿携程首页类型切换

效果 唠叨 闲来无事,不小心下载了携程app,还幻想可以去旅游一番,奈何自己运气不好,自从高考时第一次吹空调导致自己拉肚子考试,物理,数学考了一半就交卷,英语2B铅笔除了问题,导致原…

MySQL数据库的基本概念与安装

目录 引言 一、数据库的基本概念 (一)数据、表与数据库 1.数据(Data) 2.表 3.数据库 (二)数据库管理系统 (三)数据库系统 二、数据库的发展 三、主流数据库的介绍 (一)关…

一命通关深度优先遍历

前言 在解释深度优先遍历之前,我想先让大家去思考一个可能从未想过的问题: 为什么我们在学习基础数据结构的时候,都没有出现dfs和bfs这两个词,而在学习二叉树和图的时候,突然蹦出了深度优先遍历和广度优先遍历这两个…

力扣 第 126 场双周赛 解题报告 | 珂学家 | 贡献法思维场 + 贪心构造 + 0-1背包

前言 整体评价 T3是道好题,一开始思路偏了往按字母前缀和和DP去想了,但是感觉很难下手,后来发现从贡献的角度,其实和位子无关系,只需要贪心即可。 T4也是一道贡献思路题,理清核心的点,就能简单…

Vue项目使用process.env关键字及Vue.config.js配置解决前端跨域问题

1.process.env 是Node.js 中的一个环境 1.打开命令行查看环境: 2.process.env与Vue CLI 项目 Vue Cli 有以下三种运行模式 development 模式用于 vue-cli-service serve test 模式用于 vue-cli-service test:unit production 模式用于 vue-cli-service build 和 vue-cli-se…

每日一练:LeeCode-167. 两数之和 II - 输入有序数组【双指针】

给你一个下标从 1 开始的整数数组 numbers &#xff0c;该数组已按 非递减顺序排列 &#xff0c;请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] &#xff0c;则 1 < index1 < index2 < numbers.…

如何本地部署1Panel面板

文章目录 前言1. Linux 安装1Panel2. 安装cpolar内网穿透3. 配置1Panel公网访问地址4. 公网远程访问1Panel管理界面5. 固定1Panel公网地址 前言 1Panel 是一个现代化、开源的 Linux 服务器运维管理面板。高效管理,通过 Web 端轻松管理 Linux 服务器&#xff0c;包括主机监控、…

YOLOv8改进 | 主干篇 | 利用图像分割网络UNetV2改善图像分割检测性能(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是利用图像分割网络UNetV2的主干来改进我们的YOLOv8分割模型&#xff08;本文的内容虽然YOLOv8所有的功能的用户都能使用&#xff0c;但是还是建议分割的用户使用&#xff09;&#xff0c;U-Net v2 旨在改进医学图像分割的性能&#xff…