【Go Web开发】创建自定义性能参数
expvar处理程序提供的默认信息是一个好的开始。但我们可以通过在JSON响应中开放一些额外的定制指标来让它变得更有用。
为了说明自定义性能参数,我们先开始一个简单的,将应用程序的版本号添加到JSON响应中。如果你忘记了,版本号是定义在main.go文件中的字符串常量值为:"1.0.0"。
实现的代码分为两个基本步骤:1、使用expvar包注册一个自定义变量。2、需要为变量赋值。代码大致为:
expvar.NewString("version").Set(version)
代码的第一部分:expvar.NewString("version")创建一个新的expvar.String类型,然后分布到expvar处理程序的JSON响应中,名称为"version"并返回一个指向它的指针。然后我们使用Set()方法来为指针赋值。
需要注意的两个事情:
- expvar.String类型是并发安全的。因此,如果有需要,可以在你的处理程序中对其值进行处理。
- 如果你试图注册两个相同名称的expvar变量,当注册重复的变量,会导致运行时panic。
下面,我们将前面的代码集成到main()函数中,如下所示:
File: cmd/api/main.go
package main
...
expvar.NewString("version").Set(version)
// 添加models字段,引入数据库模型为接口处理程序所用
app := &application{
config: cfg,
logger: logger,
models: data.NewModels(db),
mailer: mailer.New(cfg.smtp.host, cfg.smtp.port, cfg.smtp.username, cfg.smtp.password, cfg.smtp.sender),
}
// 启动HTTP服务
err = app.server()
if err != nil {
logger.Fatal(err, nil)
}
}
...
如果你重启API服务并再次访问http://localhost:4000/debug/vars,你将在JSON中"version": "1.0.0"。如下所示:
$ 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,
...
"PauseTotalNs": 0,
"StackInuse": 458752,
"StackSys": 458752,
"Sys": 71961616,
"TotalAlloc": 334776
},
"version": "1.0.0"
}
注意:在上面的代码中我们使用expvar.NewString()函数注册和发布字符串到expvar处理程序。但是Go也为其他一些常见的数据类型提供了函数:NewFloat(), newint()和NewMap()。所有这些都以非常相似的方式工作,我们将在下一节中使用它们。
动态性能参数
有时候返回的参数需要调用其他代码或做一些预处理生成必要信息。expvar.Puhlish()函数能实现这个功能,将函数的运行结果发布到JSON响应中。
例如,你想将Go的runtime.NumGoroutine()函数返回的当前活跃的goroutine数量添加到JSON响应中,可以像下面这样写代码:
expvar.Publish("goroutine", expvar.Func(func() interface{} {
return runtime.NumGoroutine()
}))
需要指出的是这里函数返回的interface{}值必须能被正确序列化为JSON。如果无法序列化成JSON,expvar的结果中会忽略该值,GET /debug/var响应内容将不完整。任何错误都将被安静地丢弃。
在上面的代码中,runtime.NumGoroutine()返回的是一个普通int类型,将被编码为JSON数字。因此不会出现问题。
下面我们将这些代码添加到main()函数中,以及其他两个函数:
- 发布关于数据库连接池状态的信息(例如空闲和正在使用的连接数)通过db.Stats()方法。
- 以秒为单位发布当前Unix时间戳。
File:cmd/api/main.go
package main
...
expvar.NewString("version").Set(version)
//发布goroutines数量
expvar.Publish("goroutines", expvar.Func(func() interface{} {
return runtime.NumGoroutine()
}))
//发布数据库连接池数据统计
expvar.Publish("database", expvar.Func(func() interface{} {
return db.Stats()
}))
//发布当前时间戳
expvar.Publish("timestamp", expvar.Func(func() interface{} {
return time.Now().Unix()
}))
app := &application{
config: cfg,
logger: logger,
models: data.NewModels(db),
mailer: mailer.New(cfg.smtp.host, cfg.smtp.port, cfg.smtp.username, cfg.smtp.password, cfg.smtp.sender),
}
// 启动HTTP服务
err = app.server()
if err != nil {
logger.Fatal(err, nil)
}
}
...
如果你重启API服务,在浏览器中打开GET /debug/vars接口,你将看到响应的JSON值新增的"database","goroutines"和"timestamp"键值。