API 服务器健康检查和状态查询
编译并测试 API
本小节源码下载路径:demo02
思考: 服务器健康有哪些?如何检查?
磁盘空间
CPU状态
MEM状态
服务状态等
由于后期我们会实现很多路由对应的处理函数,如果量大的话,router文件会变得非常大
因此,我们也可以将处理函数放到handler目录中
”apiserver/handler/sd“
此目录将用于保存服务器检查相关处理函数
注意:短小的处理函数可以直接编写匿名函数放在router中,长函数建议拆分
apiserver/router/router.go
- // 加载模块-处理函数模块化
- "apiserver/handler/sd"
-
- // 在Load函数中添加
- // -modify here- 添加健康检查的handler
- svcd := g.Group("/sd")
- {
- svcd.GET("/health", sd.HealthCheck)
- svcd.GET("/disk", sd.DiskCheck)
- svcd.GET("/cpu", sd.CPUCheck)
- svcd.GET("/ram", sd.RAMCheck)
- }
该代码块定义了一个叫 sd 的路由分组,在该分组下注册了 /health
、/disk
、/cpu
、/ram
HTTP 路径,分别路由到 sd.HealthCheck
、sd.DiskCheck
、sd.CPUCheck
、sd.RAMCheck
函数。
sd 分组主要用来检查 API Server 的状态:健康状况、服务器硬盘、CPU 和内存使用量。
main()
函数通过调用 router.Load
函数来加载路由,路由映射到具体的处理函数
apiserver/handler/sd/check.go
编写几个检查函数
- package sd
-
- import (
- "fmt"
- "net/http"
-
- "github.com/gin-gonic/gin"
- "github.com/shirou/gopsutil/cpu"
- "github.com/shirou/gopsutil/disk"
- "github.com/shirou/gopsutil/load"
- "github.com/shirou/gopsutil/mem"
- )
-
- // 定义常量
- const (
- B = 1
- KB = 1024 * B
- MB = 1024 * KB
- GB = 1024 * MB
- )
-
- // HealthCheck shows `OK` as the ping-pong result.
- func HealthCheck(c *gin.Context) {
- message := "OK"
- // http.StatusOK => 所有HTTP状态码都对应到一个名字 (源码)
- c.String(http.StatusOK, "\n"+message)
- }
-
- // DiskCheck checks the disk usage.
- func DiskCheck(c *gin.Context) {
- // 可查看disk.Usage的源代码,此处有2个返回值,*UsageStat, erro
- u, _ := disk.Usage("/")
-
- usedMB := int(u.Used) / MB
- usedGB := int(u.Used) / GB
- totalMB := int(u.Total) / MB
- totalGB := int(u.Total) / GB
- usedPercent := int(u.UsedPercent)
-
- status := http.StatusOK
- text := "OK"
-
- if usedPercent >= 95 {
- status = http.StatusInternalServerError
- text = "CRITICAL"
- } else if usedPercent >= 90 {
- status = http.StatusTooManyRequests
- text = "WARNING"
- }
-
- message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
- c.String(status, "\n"+message)
- }
-
- // CPUCheck checks the cpu usage.
- func CPUCheck(c *gin.Context) {
- cores, _ := cpu.Counts(false)
-
- a, _ := load.Avg()
- l1 := a.Load1
- l5 := a.Load5
- l15 := a.Load15
-
- status := http.StatusOK
- text := "OK"
-
- if l5 >= float64(cores-1) {
- status = http.StatusInternalServerError
- text = "CRITICAL"
- } else if l5 >= float64(cores-2) {
- status = http.StatusTooManyRequests
- text = "WARNING"
- }
-
- message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
- c.String(status, "\n"+message)
- }
-
- // RAMCheck checks the disk usage.
- func RAMCheck(c *gin.Context) {
- u, _ := mem.VirtualMemory()
-
- usedMB := int(u.Used) / MB
- usedGB := int(u.Used) / GB
- totalMB := int(u.Total) / MB
- totalGB := int(u.Total) / GB
- usedPercent := int(u.UsedPercent)
-
- status := http.StatusOK
- text := "OK"
-
- if usedPercent >= 95 {
- status = http.StatusInternalServerError
- text = "CRITICAL"
- } else if usedPercent >= 90 {
- status = http.StatusTooManyRequests
- text = "WARNING"
- }
-
- message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
- c.String(status, "\n"+message)
- }
拓展知识:拓展知识:Go变量与内置数据类型 => 常量的定义、结构体
拓展知识:Go函数 => 错误处理
mod tidy 会自动检查依赖并下载需要的内容,非常nice
go mod tidy
这里主要是安装了:
go get github.com/shirou/gopsutil/cpu go get github.com/shirou/gopsutil/disk go get github.com/shirou/gopsutil/load go get github.com/shirou/gopsutil/mem
命令行测试:
# curl http://localhost:8000/sd/health OK
ApiPost测试:
上面我们已经实现了几个接口用于获取服务器状态,但是,它需要我们主动访问才能获取状态,那么我们如何能在有问题时,直接收到提醒呢?
定时任务/监控系统:编写监控脚本,有问题时提醒(邮件/短信/电话/微信/钉钉...)
这部分在Linux部分会详细学习...
启动服务时:主动检查,有问题直接停掉服务,提醒管理员
有时候 API 进程起来不代表 API 服务器正常,如API 进程存在,但是服务器却不能对外提供服务。因此在启动 API 服务器时,如果能够最后做一个自检会更好些。
在 apiserver 中添加了自检程序,通过自检可以最大程度地保证启动后的 API 服务器处于健康状态。
apiserver/main.go
定义pingServer用于检查/sd/health是否正常访问
- // pingServer pings the http server to make sure the router is working.
- func pingServer() error {
- //如果函数正常运行 -》返回值nil
- //如果函数出错了 -》error
- for i := 0; i < 10; i++ {
- // 请求/sd/health => Get返回值有两个
- resp, err := http.Get("http://127.0.0.1:8000" + "/sd/health")
- log.Print("Waiting for the router, retry in 1 second.")
- // 如果返回200,则表示启动成功,直接返回nil
- if err == nil && resp.StatusCode == 200 {
- return nil
- }
-
- // 否则1秒后重试
- log.Print("Waiting for the router, retry in 1 second.")
- time.Sleep(time.Second)
- }
- // 尝试10次,均失败则返回一个错误
- return errors.New("Cannot connect to the router.")
- }
在 pingServer()
函数中,http.Get
向 http://127.0.0.1:8080/sd/health
发送 HTTP GET 请求
如果函数正确执行并且返回的 HTTP StatusCode 为 200,则说明 API 服务器可用。
如果超过指定次数,服务还是不能访问,pingServer
会 返回errors,表示API服务器不可用。
拓展知识:标准库-log:Go语言标准库之log - 二十三岁的有德 - 博客园
拓展知识:标准库-time: https://www.jianshu.com/p/9d5636d34f17
拓展知识:标准库-常用的http请求操作: golang常用的http请求操作 - 腾讯云开发者社区-腾讯云
apiserver/main.go
调用pingServer检查服务是否正常
- func main() {
- ...
-
- // 调用协程函数,检查服务健康状态
在启动 HTTP 端口前 go 一个 pingServer
协程(后台并行执行的一个任务)
启动 HTTP 端口后,该协程不断地 ping /sd/health
路径
如果成功,则输出部署成功提示
如果失败次数超过一定次数,则终止 HTTP 服务器进程
拓展知识:go协程:https://www.jianshu.com/p/4ae2281927d7