”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > Golang:可观察性和分析如何揭示几乎无法检测到的节流

Golang:可观察性和分析如何揭示几乎无法检测到的节流

发布于2024-11-08
浏览:924

在 Go 的个人项目中,该项目从 Bovespa 获取金融资产信息。
该系统充分利用了 goroutine 的并发性和并行性,每 8 秒更新一次资产信息(以及业务计算)。
最初,没有出现错误或警告,但我注意到一些 goroutine 的执行时间比其他 goroutine 更长。

更具体地说,虽然p99时间为0.03 ms,但在某些时候,它增加到0.9 ms。这促使我进一步调查这个问题。

我发现我使用的是信号量 goroutine 池,它是基于 GOMAXPROCS 变量创建的。
然而,我意识到这种方法有一个问题。

当我们使用 GOMAXPROCS 变量时,它无法正确捕获容器中可用的核心数量。如果容器的可用核心数少于 VM 的总数,则它会考虑 VM 的总数。例如,我的虚拟机有 8 个可用核心,但容器只有 4 个。这导致创建 8 个 goroutine 同时运行,导致限制。

经过一夜的大量研究,我发现 Uber 开发的一个库可以更有效地自动调整 GOMAXPROCS 变量,无论它是否在容器中。事实证明,该解决方案极其稳定且高效:automaxprocs

Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável 优步Go / 自动最大进程

自动设置 GOMAXPROCS 以匹配 Linux 容器 CPU 配额。

automaxprocs Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável

自动设置 GOMAXPROCS 以匹配 Linux 容器 CPU 配额。

安装

go get -u go.uber.org/automaxprocs

快速入门

import _ "go.uber.org/automaxprocs"

func main() {
  // Your application logic here.
}
进入全屏模式 退出全屏模式

表现

数据来自 Uber 的内部负载均衡器。我们以 200% CPU 配额(即 2 个核心)运行负载均衡器:

GOMAXPROCS RPS P50(毫秒) P99.9(毫秒)
1 28,893.18 1.46 19.70
2(等于配额) 44,715.07 0.84 26.38
3 44,212.93 0.66 30.07
4 41,071.15 0.57 42.94
8 33,111.69 0.43 64.32
默认 (24) 22,191.40 0.45 76.19

当 GOMAXPROCS 增加到 CPU 配额以上时,我们看到 P50 略有下降,但看到 P99 显着增加。我们还看到处理的总 RPS 也在下降。

当 GOMAXPROCS 高于分配的 CPU 配额时,我们还看到了明显的限制:

$ cat /sys/fs/cgroup/cpu,cpuacct/system.slice/[...]/cpu.stat
nr_periods 42227334
nr_throttled 131923
throttled_time 88613212216618

一旦 GOMAXPROCS 减少到与 CPU 配额相匹配,我们就没有看到 CPU 限制。

在 GitHub 上查看

使用该库后,问题得到解决,现在p99时间一直保持在0.02ms。这一经验强调了并发系统中可观察性和分析的重要性。

以下是一个非常简单的示例,但展示了性能差异。

使用Go的原生测试和benckmak包,我创建了两个文件:

benchmarking_with_enhancement_test.go

package main

import (
    _ "go.uber.org/automaxprocs"
    "runtime"
    "sync"
    "testing"
)

// BenchmarkWithEnhancement Função com melhoria, para adicionar o indice do loop em um array de inteiro
func BenchmarkWithEnhancement(b *testing.B) {
    // Obtém o número de CPUs disponíveis
    numCPUs := runtime.NumCPU()
    // Define o máximo de CPUs para serem usadas pelo programa
    maxGoroutines := runtime.GOMAXPROCS(numCPUs)
    // Criação do semáforo
    semaphore := make(chan struct{}, maxGoroutines)

    var (
        // Espera para grupo de goroutines finalizar
        wg sync.WaitGroup
        // Propriade
        mu sync.Mutex
        // Lista para armazenar inteiros
        list []int
    )

    // Loop com mihão de indices
    for i := 0; i 



benchmarking_without_enhancement_test.go

package main

import (
    "runtime"
    "sync"
    "testing"
)

// BenchmarkWithoutEnhancement Função sem a melhoria, para adicionar o indice do loop em um array de inteiro
func BenchmarkWithoutEnhancement(b *testing.B) {
    // Obtém o número de CPUs disponíveis
    numCPUs := runtime.NumCPU()
    // Define o máximo de CPUs para serem usadas pelo programa
    maxGoroutines := runtime.GOMAXPROCS(numCPUs)
    // Criação do semáforo
    semaphore := make(chan struct{}, maxGoroutines)

    var (
        // Espera para grupo de goroutines finalizar
        wg sync.WaitGroup
        // Propriade
        mu sync.Mutex
        // Lista para armazenar inteiros
        list []int
    )

    // Loop com mihão de indices
    for i := 0; i 



它们之间的区别是使用Uber库导入。

假设使用 2 个 CPU 运行基准测试时,结果是:

Golang: Como a observabilidade e profiling revelaram um throttling quase indetectável

ns/op:提供执行特定操作所需时间的平均值(以纳秒为单位)。

请注意,我的 CPU 的可用总数为 8 个核心,这就是 runtime.NumCPU() 属性返回的值。然而,在运行基准测试时,我定义只使用两个 CPU,而没有使用 automaxprocs 的文件定义一次执行限制为 8 个 goroutine,而最高效的为 2 个,因为这样使用更少的分配可以使执行更加高效。

因此,我们的应用程序的可观察性和分析的重要性是显而易见的。

版本声明 本文转载于:https://dev.to/mggcmatheus/golang-como-a-observabilidade-e-profiling-revelaram-um-throttling-quase-indetectavel-1h5p?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-05-02
  • 在Pandas中如何将年份和季度列合并为一个周期列?
    在Pandas中如何将年份和季度列合并为一个周期列?
    pandas data frame thing commans date lay neal and pree pree'和pree pree pree”,季度 2000 q2 这个目标是通过组合“年度”和“季度”列来创建一个新列,以获取以下结果: [python中的concate...
    编程 发布于2025-05-02
  • PHP与C++函数重载处理的区别
    PHP与C++函数重载处理的区别
    作为经验丰富的C开发人员脱离谜题,您可能会遇到功能超载的概念。这个概念虽然在C中普遍,但在PHP中构成了独特的挑战。让我们深入研究PHP功能过载的复杂性,并探索其提供的可能性。在PHP中理解php的方法在PHP中,函数超载的概念(如C等语言)不存在。函数签名仅由其名称定义,而与他们的参数列表无关。...
    编程 发布于2025-05-02
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-05-02
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-05-02
  • 查找当前执行JavaScript的脚本元素方法
    查找当前执行JavaScript的脚本元素方法
    如何引用当前执行脚本的脚本元素在某些方案中理解问题在某些方案中,开发人员可能需要将其他脚本动态加载其他脚本。但是,如果Head Element尚未完全渲染,则使用document.getElementsbytagname('head')[0] .appendChild(v)的常规方...
    编程 发布于2025-05-02
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-05-02
  • C++中如何将独占指针作为函数或构造函数参数传递?
    C++中如何将独占指针作为函数或构造函数参数传递?
    在构造函数和函数中将唯一的指数管理为参数 unique pointers( unique_ptr [2启示。通过值: base(std :: simelor_ptr n) :next(std :: move(n)){} 此方法将唯一指针的所有权转移到函数/对象。指针的内容被移至功能中,在操作...
    编程 发布于2025-05-02
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-05-02
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考虑...
    编程 发布于2025-05-02
  • 如何高效地在一个事务中插入数据到多个MySQL表?
    如何高效地在一个事务中插入数据到多个MySQL表?
    mySQL插入到多个表中,该数据可能会产生意外的结果。虽然似乎有多个查询可以解决问题,但将从用户表的自动信息ID与配置文件表的手动用户ID相关联提出了挑战。使用Transactions和last_insert_id() 插入用户(用户名,密码)值('test','test...
    编程 发布于2025-05-02
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    编程 发布于2025-05-02
  • 人脸检测失败原因及解决方案:Error -215
    人脸检测失败原因及解决方案:Error -215
    错误处理:解决“ error:( - 215)!empty()in Function openCv in Function MultSiscale中的“检测”中的错误:在功能检测中。”当Face Cascade分类器(即面部检测至关重要的组件)未正确加载时,通常会出现此错误。要解决此问题,必须...
    编程 发布于2025-05-02
  • 左连接为何在右表WHERE子句过滤时像内连接?
    左连接为何在右表WHERE子句过滤时像内连接?
    左JOIN CONUNDRUM:WITCHING小时在数据库Wizard的领域中变成内在的加入很有趣,当将c.foobar条件放置在上面的Where子句中时,据说左联接似乎会转换为内部连接。仅当满足A.Foo和C.Foobar标准时,才会返回结果。为什么要变形?关键在于其中的子句。当左联接的右侧值...
    编程 发布于2025-05-02
  • Java的Map.Entry和SimpleEntry如何简化键值对管理?
    Java的Map.Entry和SimpleEntry如何简化键值对管理?
    A Comprehensive Collection for Value Pairs: Introducing Java's Map.Entry and SimpleEntryIn Java, when defining a collection where each element com...
    编程 发布于2025-05-02

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3