Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Kebocoran memori pelayan web Gin Gonic?

Kebocoran memori pelayan web Gin Gonic?

PHPz
PHPzke hadapan
2024-02-08 21:51:08571semak imbas

Gin Gonic 网络服务器内存泄漏?

editor php Baicao mendapati bahawa mungkin terdapat masalah kebocoran memori semasa menggunakan pelayan web Gin Gonic. Kebocoran memori ialah pepijat biasa yang menyebabkan program menduduki terlalu banyak sumber memori, akhirnya menjejaskan kestabilan dan prestasi sistem. Bagi pembangun, adalah penting untuk mengesan dan menyelesaikan kebocoran memori tepat pada masanya. Dalam artikel ini, kami akan meneroka punca dan penyelesaian kebocoran memori dalam pelayan rangkaian Gin Gonic, membantu pembangun mengoptimumkan kod dan meningkatkan prestasi sistem.

Kandungan soalan

Huraian

Saya mengalami potensi kebocoran memori dalam pelayan web gin-gonic/gin. Saya cuba meniru isu dengan mencipta titik akhir /health_check yang mudah. Titik akhir /health_check dipukul setiap saat. Isu ini menyebabkan keadaan kehabisan memori (OOM) apabila memori Pod yang tersedia telah habis. Tiada bekas lain yang berjalan dalam Pod.

Saya juga mendedahkan metrik pprof dan prometheus untuk memahami isu tersebut tetapi tidak menemui apa-apa. Saya tidak melihat sebarang isu tersenarai lain yang melaporkan isu yang sama, jadi saya berharap seseorang boleh membantu saya mengasingkan isu tersebut.

Saya tidak nampak sebarang peningkatan dalam ingatan timbunan atau timbunan, tetapi ingatan proses terus meningkat. Saya dapat melihat peningkatan menggunakan RSS dan blok memori yang sepadan menggunakan pmap, tetapi saya tidak dapat mengesan mengapa memori tidak dibersihkan atau untuk apa memori yang diperuntukkan digunakan.

Cara membiak

Contoh kod mudah dengan titik akhir yang berkaitan:

<code>package server

import (
    "fmt"
    "net/http"
    _ "net/http/pprof"

    "github.com/gin-gonic/gin"
    "github.com/prometheus/client_golang/prometheus/promhttp"
)

func setupRouter() *gin.Engine {
    router := gin.New()
    router.GET("/health_check", func(c *gin.Context) {
        c.String(http.StatusOK, "Hello World!")
    })
    router.GET("/debug/pprof", gin.WrapF(http.DefaultServeMux.ServeHTTP))
    router.GET("/debug/pprof/:pprofType", gin.WrapF(http.DefaultServeMux.ServeHTTP))
    router.GET("/metrics", func(c *gin.Context) {
        handler := promhttp.Handler()
        handler.ServeHTTP(c.Writer, c.Request)
    })

    return router
}

func Start() {
    router := setupRouter()
    err := router.Run(":8080")
    if err != nil {
        fmt.Printf("Error starting server: %v\n", err)
    }
}
</code>
<code>package main

import (
    "example.com/health-check/server"
)

func main() {
    server.Start()
}
</code>

Bina arahan:

go build -ldflags="-s -w" -race -o health-check main.go

Had sumber pod:

  1. 64MiB RAM
  2. 1 CPU

Jangkaan

Saya menjangkakan penggunaan memori akan kekal konsisten. Beberapa turun naik boleh diterima, tetapi saya mahu penggunaan memori kekal konsisten dan tidak kehabisan ingatan.

Hasil sebenar

Pod ranap disebabkan OOM yang terus meningkatkan ingatan selama kira-kira 90 minit. Setiap lonjakan dalam graf di bawah mewakili permulaan semula Pod disebabkan oleh OOM.

Coroutine

goroutine 8334 [running]:
runtime/pprof.writeGoroutineStacks({0x7f0e9e220b20, 0xc000506200})
    /usr/local/go/src/runtime/pprof/pprof.go:703 +0x75
runtime/pprof.writeGoroutine({0x7f0e9e220b20, 0xc000506200}, 0x2)
    /usr/local/go/src/runtime/pprof/pprof.go:692 +0x45
runtime/pprof.(*Profile).WriteTo(0x1509f00, {0x7f0e9e220b20, 0xc000506200}, 0xc?)
    /usr/local/go/src/runtime/pprof/pprof.go:329 +0x1b1
net/http/pprof.handler.ServeHTTP({0xc000051601, 0x9}, {0x7f0e5462fd18, 0xc000506200}, 0x0?)
    /usr/local/go/src/net/http/pprof/pprof.go:267 +0x58a
net/http/pprof.Index({0x7f0e5462fd18?, 0xc000506200}, 0xc000506600)
    /usr/local/go/src/net/http/pprof/pprof.go:384 +0x129
net/http.HandlerFunc.ServeHTTP(0xf53ac8, {0x7f0e5462fd18, 0xc000506200}, 0xc0000b0500?)
    /usr/local/go/src/net/http/server.go:2136 +0x48
net/http.(*ServeMux).ServeHTTP(0xc0004a3740?, {0x7f0e5462fd18, 0xc000506200}, 0xc000506600)
    /usr/local/go/src/net/http/server.go:2514 +0xbd
example.com/health-check/server.setupRouter.WrapF.func4(0xc000506200)
    /go/pkg/mod/github.com/gin-gonic/[email&#160;protected]/utils.go:42 +0x97
github.com/gin-gonic/gin.(*Context).Next(...)
    /go/pkg/mod/github.com/gin-gonic/[email&#160;protected]/context.go:174
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0001871e0, 0xc000506200)
    /go/pkg/mod/github.com/gin-gonic/[email&#160;protected]/gin.go:620 +0xb91
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0001871e0, {0x105b2e0?, 0xc00027e540}, 0xc000506600)
    /go/pkg/mod/github.com/gin-gonic/[email&#160;protected]/gin.go:576 +0x425
net/http.serverHandler.ServeHTTP({0xc000600db0?}, {0x105b2e0, 0xc00027e540}, 0xc000506600)
    /usr/local/go/src/net/http/server.go:2938 +0x2a2
net/http.(*conn).serve(0xc000240900, {0x105c478, 0xc0001ad5c0})
    /usr/local/go/src/net/http/server.go:2009 +0xc25
created by net/http.(*Server).Serve in goroutine 1
    /usr/local/go/src/net/http/server.go:3086 +0x80d

goroutine 1 [IO wait]:
internal/poll.runtime_pollWait(0x7f0e9ea9d410, 0x72)
    /usr/local/go/src/runtime/netpoll.go:343 +0x85
internal/poll.(*pollDesc).wait(0xc0003a62a0, 0x4ae001?, 0x0)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0xb1
internal/poll.(*pollDesc).waitRead(...)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Accept(0xc0003a6280)
    /usr/local/go/src/internal/poll/fd_unix.go:611 +0x405
net.(*netFD).accept(0xc0003a6280)
    /usr/local/go/src/net/fd_unix.go:172 +0x3e
net.(*TCPListener).accept(0xc00024d480)
    /usr/local/go/src/net/tcpsock_posix.go:152 +0x3e
net.(*TCPListener).Accept(0xc00024d480)
    /usr/local/go/src/net/tcpsock.go:315 +0x65
net/http.(*Server).Serve(0xc00054c000, {0x105b520, 0xc00024d480})
    /usr/local/go/src/net/http/server.go:3056 +0x57f
net/http.(*Server).ListenAndServe(0xc00054c000)
    /usr/local/go/src/net/http/server.go:2985 +0xbd
net/http.ListenAndServe(...)
    /usr/local/go/src/net/http/server.go:3239
github.com/gin-gonic/gin.(*Engine).Run(0xc0001871e0, {0xc0003bbef8, 0x1, 0x1})
    /go/pkg/mod/github.com/gin-gonic/[email&#160;protected]/gin.go:386 +0x38d
example.com/health-check/server.Start()
    /app/server/server.go:49 +0x52
main.main()
    /app/main.go:8 +0x1d

goroutine 82 [IO wait]:
internal/poll.runtime_pollWait(0x7f0e9ea9d318, 0x72)
    /usr/local/go/src/runtime/netpoll.go:343 +0x85
internal/poll.(*pollDesc).wait(0xc0002c60a0, 0xc000568000?, 0x0)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0xb1
internal/poll.(*pollDesc).waitRead(...)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002c6080, {0xc000568000, 0x1000, 0x1000})
    /usr/local/go/src/internal/poll/fd_unix.go:164 +0x3e5
net.(*netFD).Read(0xc0002c6080, {0xc000568000, 0x1000, 0x1000})
    /usr/local/go/src/net/fd_posix.go:55 +0x4b
net.(*conn).Read(0xc000514010, {0xc000568000, 0x1000, 0x1000})
    /usr/local/go/src/net/net.go:179 +0xad
net/http.(*connReader).Read(0xc0002c4450, {0xc000568000, 0x1000, 0x1000})
    /usr/local/go/src/net/http/server.go:791 +0x2b5
bufio.(*Reader).fill(0xc000536d20)
    /usr/local/go/src/bufio/bufio.go:113 +0x29a
bufio.(*Reader).Peek(0xc000536d20, 0x4)
    /usr/local/go/src/bufio/bufio.go:151 +0xc7
net/http.(*conn).serve(0xc0002e21b0, {0x105c478, 0xc0001ad5c0})
    /usr/local/go/src/net/http/server.go:2044 +0xe7c
created by net/http.(*Server).Serve in goroutine 1
    /usr/local/go/src/net/http/server.go:3086 +0x80d

goroutine 8335 [IO wait]:
internal/poll.runtime_pollWait(0x7f0e9ea9d128, 0x72)
    /usr/local/go/src/runtime/netpoll.go:343 +0x85
internal/poll.(*pollDesc).wait(0xc0002c62a0, 0xc000600dc1?, 0x0)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:84 +0xb1
internal/poll.(*pollDesc).waitRead(...)
    /usr/local/go/src/internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002c6280, {0xc000600dc1, 0x1, 0x1})
    /usr/local/go/src/internal/poll/fd_unix.go:164 +0x3e5
net.(*netFD).Read(0xc0002c6280, {0xc000600dc1, 0x1, 0x1})
    /usr/local/go/src/net/fd_posix.go:55 +0x4b
net.(*conn).Read(0xc0000b04d0, {0xc000600dc1, 0x1, 0x1})
    /usr/local/go/src/net/net.go:179 +0xad
net/http.(*connReader).backgroundRead(0xc000600db0)
    /usr/local/go/src/net/http/server.go:683 +0x83
created by net/http.(*connReader).startBackgroundRead in goroutine 8334
    /usr/local/go/src/net/http/server.go:679 +0x246

Persekitaran

  • Versi Go: go1.21.1 linux/amd64
  • imej buruh pelabuhan: 1.21.1-bullseye
  • versi gin (atau buat rujukan): github.com/gin-gonic/gin v1.9.1
  • Sistem Pengendalian: Debian GNU/Linux 11 (Bulls Eye)
  • GOGC=10;GOMEMLIMIT=64MiB (Juga cuba menggunakan lalai, tetapi hasil yang sama)

Terima kasih atas sebarang bantuan atau bimbingan, amat dihargai.

Penyelesaian

Jadi saya melakukan beberapa eksperimen untuk mengetahui di mana daya ingatan meningkat.

Saya cuba menguji tanpa bendera binaan -race dan ingatan kelihatan baik sekarang. Saya tidak nampak ia meningkat secara konsisten pada pelayan terbiar (hanya keaktifan, probe kesediaan dan titik akhir metrik tersedia).

Saya tidak pasti sepenuhnya mengapa ini berlaku, atau jika ini dijangka, tetapi saya akan menelitinya dengan lebih mendalam dan telah mengalih keluar bendera buat sementara waktu daripada penempatan pengeluaran kami.

Kongsi trend memori selepas mengalih keluar bendera ini (arahan bina ialah go build), puncaknya ialah ujian beban yang saya jalankan, mencetuskan permintaan 1M (100 selari pada satu masa):

Berikut ialah penggunaan memori Pod:

Berikut ialah RSS:

Berikut ialah penggunaan memori timbunan dan tindanan:

PS: Saya menandai ini sebagai jawapan buat masa ini, tetapi jangan ragu untuk membetulkan saya, saya masih baru dalam golang dan terima kasih banyak atas sumbangan semua orang. Jika saya mendapati sesuatu yang bertentangan dengan keputusan ini, saya akan mengemas kini/memadam jawapan ini, terima kasih.

Atas ialah kandungan terperinci Kebocoran memori pelayan web Gin Gonic?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam