Rumah > Artikel > pembangunan bahagian belakang > Kebocoran memori pelayan web 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.
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.
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:
Saya menjangkakan penggunaan memori akan kekal konsisten. Beberapa turun naik boleh diterima, tetapi saya mahu penggunaan memori kekal konsisten dan tidak kehabisan ingatan.
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 protected]/utils.go:42 +0x97 github.com/gin-gonic/gin.(*Context).Next(...) /go/pkg/mod/github.com/gin-gonic/[email protected]/context.go:174 github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc0001871e0, 0xc000506200) /go/pkg/mod/github.com/gin-gonic/[email protected]/gin.go:620 +0xb91 github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc0001871e0, {0x105b2e0?, 0xc00027e540}, 0xc000506600) /go/pkg/mod/github.com/gin-gonic/[email 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 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
Terima kasih atas sebarang bantuan atau bimbingan, amat dihargai.
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 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!