Golang程序运行卡住了怎么办

Golang程序运行卡住了怎么办 大家好,

我最近开始使用Go语言,并尝试将其与Redis集成。因此我使用了radix库来实现这一目标,但我的程序时不时会卡住,我无法找出导致这种情况的原因。我尝试使用pprof进行更深入的分析,但未能获得任何可以确定卡住原因的结果。

我发现几乎100%的CPU时间都消耗在runtime.gopark上:

类型:goroutine 显示节点占110总计的110,100% flat flat% sum% ■■■ ■■■% 108 98.18% 98.18% 108 98.18% runtime.gopark 1 0.91% 99.09% 1 0.91% runtime.notetsleepg 1 0.91% 100% 1 0.91% runtime/pprof.writeRuntimeProfile

当我使用pprof进行性能分析时,也发现100%的时间消耗在runtime.usleep上:

持续时间:10秒,总样本=10毫秒(0.1%) 显示节点占10毫秒总计的10毫秒,100% flat flat% sum% ■■■ ■■■% 10毫秒 100% 100% 10毫秒 100% runtime.usleep 0 0% 100% 10毫秒 100% runtime.mstart 0 0% 100% 10毫秒 100% runtime.mstart1 0 0% 100% 10毫秒 100% runtime.sysmon

pprof的互斥锁和阻塞分析没有返回任何结果。

这是堆输出:

文件:gw 类型:使用中空间 时间:UTC时间2019年7月7日晚上10点23分 显示节点占5370.73KB总计的5370.73KB,100% ----------------------------------------------------------±------------ flat flat% sum% ■■■ ■■■% calls calls% + 上下文 ----------------------------------------------------------±------------ 1028KB 50.00% | bufio.NewWriter 1028KB 50.00% | github.com/valyala/fasthttp.acquireWriter 2056.01KB 38.28% 38.28% 2056.01KB 38.28% | bufio.NewWriterSize ----------------------------------------------------------±------------ 1778.29KB 100% | compress/flate.NewWriter 1195.29KB 22.26% 60.54% 1778.29KB 33.11% | compress/flate.(*compressor).init 583.01KB 32.78% | compress/flate.newDeflateFast ----------------------------------------------------------±------------ 583.01KB 100% | compress/flate.(*compressor).init 583.01KB 10.86% 71.39% 583.01KB 10.86% | compress/flate.newDeflateFast ----------------------------------------------------------±------------

我还尝试检查是否是Redis的问题,因此创建了一个客户端进行调用以确认其仍然可用,看起来问题并不在那里。

有没有什么建议可以帮助我更好地分析这个问题?


更多关于Golang程序运行卡住了怎么办的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang程序运行卡住了怎么办的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


根据你提供的pprof数据,程序卡住的主要原因是Goroutine被过度调度到休眠状态。runtime.gopark占用98.18%的CPU时间表明大量Goroutine处于等待状态,而runtime.usleep占100%说明系统监控线程也在空转。这通常是由以下原因导致的:

1. 连接池资源耗尽

radix库的连接池可能已耗尽,导致Goroutine在等待可用连接时被挂起。检查连接池配置:

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/mediocregopher/radix/v4"
)

func main() {
    // 配置连接池
    pool, err := radix.PoolConfig{
        Size: 10, // 连接池大小
        Dial: func(ctx context.Context, network, addr string) (radix.Conn, error) {
            return radix.Dial(ctx, network, addr, radix.DialTimeout(5*time.Second))
        },
    }.New(context.Background(), "tcp", "localhost:6379")
    
    if err != nil {
        panic(err)
    }
    defer pool.Close()

    // 使用连接时设置超时
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    var result string
    err = pool.Do(ctx, radix.Cmd(&result, "PING"))
    if err != nil {
        fmt.Printf("Redis error: %v\n", err)
        return
    }
    fmt.Printf("Redis response: %s\n", result)
}

2. 死锁检测

添加死锁检测来识别潜在的同步问题:

package main

import (
    "runtime"
    "time"
)

func detectDeadlock() {
    go func() {
        for {
            // 定期输出Goroutine数量
            num := runtime.NumGoroutine()
            if num > 1000 { // 设置合理的阈值
                println("警告:Goroutine数量异常:", num)
            }
            time.Sleep(5 * time.Second)
        }
    }()
}

3. 使用更详细的pprof分析

启用所有pprof分析类型:

package main

import (
    "net/http"
    _ "net/http/pprof"
    "time"
)

func startProfiling() {
    go func() {
        println("PPROF available at http://localhost:6060/debug/pprof")
        http.ListenAndServe("localhost:6060", nil)
    }()
    
    // 生成Goroutine阻塞分析
    runtime.SetBlockProfileRate(1)
    runtime.SetMutexProfileFraction(1)
}

func main() {
    startProfiling()
    
    // 你的主程序逻辑
    for {
        time.Sleep(time.Hour)
    }
}

4. 检查Redis操作超时

确保所有Redis操作都有合理的超时设置:

func performRedisOperation(pool radix.Client) error {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
    
    var value string
    err := pool.Do(ctx, radix.Cmd(&value, "GET", "some-key"))
    if err != nil {
        if err == context.DeadlineExceeded {
            println("Redis操作超时")
        }
        return err
    }
    return nil
}

5. Goroutine泄漏检测

添加Goroutine泄漏检测:

package main

import (
    "runtime"
    "time"
)

func monitorGoroutines() {
    initial := runtime.NumGoroutine()
    
    go func() {
        for {
            current := runtime.NumGoroutine()
            if current > initial * 2 { // 如果Goroutine数量翻倍
                println("可能的Goroutine泄漏:", current)
                
                // 输出当前所有Goroutine的堆栈
                buf := make([]byte, 1<<20)
                stacklen := runtime.Stack(buf, true)
                println(string(buf[:stacklen]))
            }
            time.Sleep(10 * time.Second)
        }
    }()
}

主要问题很可能是连接池资源耗尽或Redis操作缺乏超时控制,导致Goroutine无限期等待。建议优先检查连接池配置和所有网络操作的超时设置。

回到顶部