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')统计访问量。这种方法适合需要长期分析和报表的场景。