用过nginx的人都知道在nginx中有一个status页面,可以查看到服务器当前的一些状态信息。本篇将介绍一下怎么在go中加上相似的功能,并在status页面上实现一个简单的实时统计功能。
go开启status页面
要在go中开启一个status页面很简单,在http.HandleFunc中判断请求的URI,如果等于“/status”,则调用statusHandler处理:
//Dispatch在入口的http.HandleFunc中调用
func (this *App) Dispatch(w http.ResponseWriter, r *http.Request, c EcgoApper){
if this.statusOn && strings.ToLower(r.RequestURI) == "/stauts" { //开启了status页面且请求路径为status的处理
this.statsHandler()
return
}
}
//显示status页面
func (this *App)statusHandler(){
fmt.Fprintf(this.ResWriter, "status:<br/>%v",this.status)
//实际应用可用特定的模板美化
//status的内容包括: uptime,pv(总pv,当前周期pv,周期pv的峰值),traffic(总流量,当前周期流量,周期流量峰值)
}
status的数据结构
nginx的status主要显示连接数相关,我对go的net包不熟,先不处理连接数,改为统计pv和流量,从而实现一个简单的实时统计服务,包括:
-
从服务启动开始算的总pv,以指定时间为周期(比如5分钟)统计每个周期的pv数,并记录周期pv数的最大值,和当前周期的值
-
以同样方式,统计总流量、当前周期流量和周期流量的最大值。
status结构定义:
//计数器
type counter struct {
interval int64 //切换的时间长度(s)
time int64 //上次切换的时间点
total int64 //总数
num int64 //当前周期计数
max_num int64 //周期计数的最大值
}
type status struct {
uptime time.Time //启动的时间
pv *counter
traffic *counter
}
status的操作方法:
//计数器增加num
func (this *counter) add(num int64) {
this.total += num
now := time.Now().Unix()
if now > this.time+this.interval { //周期结束,进入另一周期,重置周期数据num
this.time = now
this.num = num
} else {
this.num += num
if this.max_num < this.num {
this.max_num = this.num
}
}
}
//status增加计数
func (this *App) statsIncrease() {
this.status.pv.add(1) //pv+1
this.status.traffic.add(int64(this.ResWriter.Length)) //流量增加, this.ResWriter.Length为当次请求的响应大小
}
使用status进行实时统计
status已经准备好了,下面看看如何使用:
- 首先在应用启动时(注意不是在请求到达时),创建一个status对象
func newStats() *status {
interval, _ := 300 //实际应用时,从配置读
return &status{
uptime: time.Now(),
pv: &counter{interval: interval},
traffic: &counter{interval: interval},
}
}
- 然后在每次请求结束时,调用statusIncrease()方法来使pv和流量增加
func (this *App) reqFinish() {
this.statusIncrease()
//其它处理(比如access_log)
}
完成这些后,请求/status页面,将看到实时的pv和流量统计数据了
net/http/pprof
go还有一个功能强大的pprof,可以用来查看程序详细的运行状, 有空再研究。