【Go Web 開發】性能參數
當你的應用程序在生產環境中運行並處理實際請求時,如果您正在進行有針對性的負載測試,您可能想要深入瞭解程序是如何執行的,以及它正在使用哪些資源。
例如,你可能想回答以下問題:
-
應用程序使用了多少內存?如何隨着時間變化的?
-
有多少 goroutine 在使用?如何隨着時間變化的?
-
有多少數據庫連接在使用中,有多少是空閒的?需要更改連接池設置嗎?
-
HTTP 成功響應客戶端和服務器錯誤的比率是多少?錯誤率是否高於正常水平?
深入瞭解這些內容可以幫助您瞭解軟硬件的配置選擇,並作爲潛在問題 (如內存泄漏) 的早期預警信號。
爲了幫助實現這一點,Go 的標準庫包含了 expvar 包,它可以讓你在運行時很容易地整理和查看不同的應用程序性能指標。
在本文你將學習到:
-
如何使用 expvar 包通過 HTTP 處理程序以 JSON 格式查看應用程序性能指標。
-
可用的默認應用程序指標是什麼,以及如何創建自定義應用程序指標,以監視 goroutines 和數據庫連接池的數量。
-
如何使用中間件監視請求級的應用程序指標,包括不同 HTTP 狀態碼的計數。
使用 Expvar 開放性能參數
由於 expvar 包提供了 expvar.handler() 函數,該函數返回一個應用程序指標的 HTTP 處理程序,因此查看 API 服務的指標變得很容易。
默認情況下,這個處理程序顯示關於內存使用的信息,以及啓動應用程序時使用的命令行參數,所有這些都以 JSON 格式輸出。
所以我們要做的第一件事就是把這個 handler 處理函數掛載到一個新的 GET /debug/vars 接口,像這樣:
File:cmd/api/routes.go
package main
...
func (app *application) routes() http.Handler {
router := httprouter.New()
router.NotFound = http.HandlerFunc(app.notFoundResponse)
router.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowedResponse)
router.HandlerFunc(http.MethodGet, "/v1/healthcheck", app.healthcheckHandler)
router.HandlerFunc(http.MethodGet, "/v1/movies", app.requirePermission("movies:read", app.listMoviesHandler))
router.HandlerFunc(http.MethodPost, "/v1/movies", app.requirePermission("movies:write", app.createMovieHandler))
router.HandlerFunc(http.MethodGet, "/v1/movies/:id", app.requirePermission("movies:read", app.showMovieHandler))
router.HandlerFunc(http.MethodPatch, "/v1/movies/:id", app.requirePermission("movies:write", app.updateMovieHandler))
router.HandlerFunc(http.MethodDelete, "/v1/movies/:id", app.requirePermission("movies:write", app.deleteMovieHandler))
router.HandlerFunc(http.MethodPost, "/v1/users", app.registerUserHandler)
router.HandlerFunc(http.MethodPut, "/v1/users/activated", app.activateUserHandler)
router.HandlerFunc(http.MethodPost, "/v1/tokens/authentication", app.createAuthenticationTokenHandler)
//註冊新的接口指向expvar處理程序
router.Handler(http.MethodGet, "/debug/vars", expvar.Handler())
return app.recoverPanic(app.enableCORS(app.rateLimit(app.authenticate(router))))
}
提示:使用 GET /degbu/var 作爲 expvar 處理程序的 Endpoint 是慣例用法不是必須的。如果你不喜歡,可以使用其他接口名稱例如 GET /v/metrics 來代替。
下面來測試下,啓動應用程序,爲了演示傳入一些命令行參數:
$ go run ./cmd/api -limiter-enable=false -port=4000
{"level":"INFO","time":"2022-01-12T03:16:43Z","message":"database connection pool established"}
{"level":"INFO","time":"2022-01-12T03:16:43Z","message":"starting server","properties":{"addr":":4000","env":"development"}}
如果你訪問 http://localhost:4000/debug/vars,將看到 JSON 響應內容包含應用程序運行時的指標信息:
$curl http://localhost:4000/debug/vars -s | python -m json.tool
{
"cmdline": [
"/var/folders/x6/8wtj7zfd7r59wpk5fmjln2p40000gn/T/go-build3315447716/b001/exe/api",
"-limiter-enable=false",
"-port=4000"
],
"memstats": {
"Alloc": 545376,
"BuckHashSys": 3860,
"BySize": [
{
"Frees": 0,
"Mallocs": 0,
"Size": 0
},
...
}
],
"DebugGC": false,
"EnableGC": true,
"Frees": 177,
"GCCPUFraction": 0,
"GCSys": 4011248,
"HeapAlloc": 545376,
"HeapIdle": 65167360,
"HeapInuse": 1482752,
"HeapObjects": 2236,
"HeapReleased": 64937984,
"HeapSys": 66650112,
"LastGC": 0,
"Lookups": 0,
"MCacheInuse": 9600,
"MCacheSys": 16384,
"MSpanInuse": 37536,
"MSpanSys": 49152,
"Mallocs": 2413,
"NextGC": 4473924,
"NumForcedGC": 0,
"NumGC": 0,
"OtherSys": 1034252,
"PauseEnd": [
可以看到 JSON 包含兩個頂層鍵值分別爲:"cmdline" 和 "memstats"。我們簡單瞭解下這些鍵。
"cmdline" 鍵包含應用程序的命令行參數列表,以程序名稱爲首元素。這本質上是 os.Args 變量的 JSON 表示,如果您想要確切地瞭解在啓動應用程序時使用了哪些非默認設置,那麼它很有用。
"memstats" 鍵包含即時內存使用快照,是由 runtime.MemStats() 函數返回的。關於這些值得文檔和描述可以查看這裏 [https://golang.org/pkg/runtime/#MemStats],但最重要的是下面幾個:
-
TotalAlloc:在堆上分配的累積字節數 (不會減少)。
-
HeadAlloc:當前堆上的字節數。
-
HeadObjects:當前堆上對象數。
-
Sys:從操作系統獲得的總內存字節 (即 Go 運行時爲堆、棧和其他內部數據結構保留的總內存)。
-
NumGC:已完成的垃圾收集器循環數。
-
NextGC:啓動下一個垃圾收集的目標堆大小 (Go 的目標是保持 HeapAlloc≤NextGC)。
提示:如果上面有你不熟悉的字段,我強烈建議你閱讀 "Understanding Allocations in Go" 文章介紹了 Go 如何分配內存以及堆棧的概念。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/UBSLoVqTVpnXmHhuIBYOyA