1. 使用Prometheus客戶端庫實現應用層訪問量統計
Prometheus是Golang生態中常用的監控解決方案,通過其客戶端庫可以輕松實現訪問量的精準統計與可視化。首先,安裝Prometheus客戶端庫:go get github.com/prometheus/client_golang/prometheus
。接著,定義一個CounterVec
類型的指標(支持按維度拆分,如HTTP方法、端點),并在應用啟動時注冊:
var httpRequests = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "http_requests_total",
Help: "Total HTTP requests",
},
[]string{"method", "endpoint"}, // 按HTTP方法和端點拆分
)
prometheus.MustRegister(httpRequests)
在HTTP請求處理器中,通過WithLabelValues
方法遞增對應維度的計數器:
func handler(w http.ResponseWriter, r *http.Request) {
httpRequests.WithLabelValues(r.Method, r.URL.Path).Inc() // 遞增當前請求的計數
w.Write([]byte("OK"))
}
最后,暴露/metrics
端點供Prometheus抓取指標:
http.Handle("/metrics", promhttp.Handler())
http.ListenAndServe(":8080", nil)
通過Prometheus的查詢語言(PromQL),可輕松獲取總訪問量(sum(http_requests_total)
)或按維度篩選的訪問量(如http_requests_total{method="GET", endpoint="/home"}
)。
2. 原子操作實現簡單計數器
對于不需要復雜維度的訪問量統計(如全局總請求數),可以使用Go的sync/atomic
包實現高性能的原子計數器。定義一個全局變量:
var requestCount uint64
在請求處理器中,通過atomic.AddUint64
遞增計數器:
func handler(w http.ResponseWriter, r *http.Request) {
atomic.AddUint64(&requestCount, 1) // 原子遞增
fmt.Fprintf(w, "Total requests: %d", requestCount)
}
這種方法無鎖,性能極高,適合高并發場景,但無法區分請求的維度(如方法、端點)。
3. 結構化日志記錄訪問量
使用結構化日志庫(如zap
、logrus
)將訪問量信息記錄到日志中,便于后續通過日志分析工具(如Loki、Elasticsearch)進行統計。以zap
為例,首先安裝庫:go get go.uber.org/zap
。在請求處理器中,記錄包含訪問信息的結構化日志:
logger, _ := zap.NewProduction()
defer logger.Sync()
logger.Info("Request received",
zap.String("method", r.Method),
zap.String("endpoint", r.URL.Path),
zap.String("remote_addr", r.RemoteAddr),
)
通過日志分析工具,可使用類似sum by(method, endpoint) (count_over_time({job="golang-app"}[1h]))
的查詢,統計指定時間范圍內的訪問量。
4. 實時讀取日志文件統計訪問量
若應用已將訪問日志輸出到文件(如Nginx的access.log
),可使用github.com/hpcloud/tail
庫實時讀取日志并統計。首先安裝庫:go get github.com/hpcloud/tail
。編寫代碼實時解析日志中的IP地址(或其他維度),并統計訪問次數:
t, err := tail.TailFile("/var/log/nginx/access.log", tail.Config{Follow: true})
if err != nil {
log.Fatal(err)
}
visitCount := make(map[string]int)
re := regexp.MustCompile(`\d+\.\d+\.\d+\.\d+`) // 匹配IP地址的正則表達式
for line := range t.Lines {
ip := re.FindString(line.Text)
if ip != "" {
visitCount[ip]++
fmt.Printf("IP: %s, Visits: %d\n", ip, visitCount[ip])
}
}
這種方法適合實時監控外部流量的訪問情況,但需確保日志格式的一致性。
5. 數據庫持久化存儲訪問量
對于需要長期存儲和復雜查詢的訪問量統計,可將數據保存到數據庫(如MySQL)中。使用xorm
等ORM庫定義日志表結構(如TrafficLog
表,包含ip
、method
、endpoint
、timestamp
等字段),并通過批量插入優化性能。編寫日志生成模塊生成模擬日志,通過通道傳遞給消費者,消費者批量寫入數據庫:
type TrafficLog struct {
Id int64 `xorm:"pk autoincr"`
Ip string `xorm:"varchar(15)"`
Method string `xorm:"varchar(10)"`
Endpoint string `xorm:"varchar(100)"`
Timestamp time.Time `xorm:"created"`
}
func batchInsertLogs(engine *xorm.Engine, logs []TrafficLog) {
_, err := engine.Insert(&logs)
if err != nil {
fmt.Printf("批量插入失敗: %v\n", err)
}
}
之后,可通過SQL查詢(如SELECT COUNT(*) FROM traffic_log WHERE endpoint = '/home'
)統計訪問量。這種方法適合需要長期分析和報表的場景。