From 897c3cddcd250c1e8bb421ae3c0771e42dc4541d Mon Sep 17 00:00:00 2001 From: 0xJacky Date: Tue, 22 Feb 2022 00:17:59 +0800 Subject: [PATCH] use goroutine to record cpu usage --- frontend/src/api/analytic.js | 9 ++ frontend/src/api/index.js | 4 +- frontend/src/views/dashboard/DashBoard.vue | 21 ++-- main.go | 109 +++++++++++---------- server/api/analytic.go | 8 ++ server/router/routers.go | 1 + server/tool/cpu_usage.go | 45 +++++++++ 7 files changed, 134 insertions(+), 63 deletions(-) create mode 100644 frontend/src/api/analytic.js create mode 100644 server/tool/cpu_usage.go diff --git a/frontend/src/api/analytic.js b/frontend/src/api/analytic.js new file mode 100644 index 00000000..629641e2 --- /dev/null +++ b/frontend/src/api/analytic.js @@ -0,0 +1,9 @@ +import http from '@/lib/http' + +const analytic = { + cpu_usage() { + return http.get('/analytic/cpu') + } +} + +export default analytic diff --git a/frontend/src/api/index.js b/frontend/src/api/index.js index 092cd39b..eced5e37 100644 --- a/frontend/src/api/index.js +++ b/frontend/src/api/index.js @@ -3,11 +3,13 @@ import config from './config' import auth from './auth' import user from './user' import install from './install' +import analytic from './analytic' export default { domain, config, auth, user, - install + install, + analytic } diff --git a/frontend/src/views/dashboard/DashBoard.vue b/frontend/src/views/dashboard/DashBoard.vue index 8fe9424a..991bf9d9 100644 --- a/frontend/src/views/dashboard/DashBoard.vue +++ b/frontend/src/views/dashboard/DashBoard.vue @@ -10,10 +10,16 @@ % -

Uptime {{ uptime }}

-

Load Averages: 1min:{{ loadavg?.load1?.toFixed(2) }} | +

+ Uptime + {{ uptime }} +

+

+ Load Averages: + 1min:{{ loadavg?.load1?.toFixed(2) }} | 5min:{{ loadavg?.load5?.toFixed(2) }} | - 15min:{{ loadavg?.load15?.toFixed(2) }}

+ 15min:{{ loadavg?.load15?.toFixed(2) }} +

@@ -120,11 +126,10 @@ export default { + btoa(this.$store.state.user.token)) this.websocket.onmessage = this.wsOnMessage this.websocket.onopen = this.wsOpen - const time = new Date() - for (let i = 200; i > 0; i--) { - this.cpu_analytic.datasets[0].data.push({x: time-i*1000, y: 0}) - this.cpu_analytic.datasets[1].data.push({x: time-i*1000, y: 0}) - } + this.$api.analytic.cpu_usage().then(r => { + this.cpu_analytic.datasets[0].data.concat(r.user) + this.cpu_analytic.datasets[1].data.concat(r.total) + }) }, destroyed() { this.websocket.close() diff --git a/main.go b/main.go index ae820fc9..bb383e59 100644 --- a/main.go +++ b/main.go @@ -1,71 +1,72 @@ package main import ( - "context" - "flag" - "github.com/0xJacky/Nginx-UI/server/model" - "github.com/0xJacky/Nginx-UI/server/router" - "github.com/0xJacky/Nginx-UI/server/settings" - tool2 "github.com/0xJacky/Nginx-UI/server/tool" - "github.com/gin-gonic/gin" - "log" - "mime" - "net/http" - "os/signal" - "syscall" - "time" + "context" + "flag" + "github.com/0xJacky/Nginx-UI/server/model" + "github.com/0xJacky/Nginx-UI/server/router" + "github.com/0xJacky/Nginx-UI/server/settings" + "github.com/0xJacky/Nginx-UI/server/tool" + "github.com/gin-gonic/gin" + "log" + "mime" + "net/http" + "os/signal" + "syscall" + "time" ) func main() { - // Create context that listens for the interrupt signal from the OS. - ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) - defer stop() + // Create context that listens for the interrupt signal from the OS. + ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + defer stop() - // Hack: fix wrong Content Type of .js file on some OS platforms - // See https://github.com/golang/go/issues/32350 - _ = mime.AddExtensionType(".js", "text/javascript; charset=utf-8") + // Hack: fix wrong Content Type of .js file on some OS platforms + // See https://github.com/golang/go/issues/32350 + _ = mime.AddExtensionType(".js", "text/javascript; charset=utf-8") - var confPath string - flag.StringVar(&confPath, "config", "app.ini", "Specify the configuration file") - flag.Parse() + var confPath string + flag.StringVar(&confPath, "config", "app.ini", "Specify the configuration file") + flag.Parse() - gin.SetMode(settings.ServerSettings.RunMode) - - settings.Init(confPath) - log.Printf("nginx config dir path: %s", tool2.GetNginxConfPath("")) - if "" != settings.ServerSettings.JwtSecret { - model.Init() - go tool2.AutoCert() - } + gin.SetMode(settings.ServerSettings.RunMode) - srv := &http.Server{ - Addr: ":" + settings.ServerSettings.HttpPort, - Handler: router.InitRouter(), - } + settings.Init(confPath) + log.Printf("nginx config dir path: %s", tool.GetNginxConfPath("")) + if "" != settings.ServerSettings.JwtSecret { + model.Init() + go tool.AutoCert() + go tool.RecordCpuUsage() + } - // Initializing the server in a goroutine so that - // it won't block the graceful shutdown handling below - go func() { - if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("listen: %s\n", err) - } - }() + srv := &http.Server{ + Addr: ":" + settings.ServerSettings.HttpPort, + Handler: router.InitRouter(), + } - // Listen for the interrupt signal. - <-ctx.Done() + // Initializing the server in a goroutine so that + // it won't block the graceful shutdown handling below + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Fatalf("listen: %s\n", err) + } + }() - // Restore default behavior on the interrupt signal and notify user of shutdown. - stop() - log.Println("shutting down gracefully, press Ctrl+C again to force") + // Listen for the interrupt signal. + <-ctx.Done() - // The context is used to inform the server it has 5 seconds to finish - // the request it is currently handling - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - if err := srv.Shutdown(ctx); err != nil { - log.Fatal("Server forced to shutdown: ", err) - } + // Restore default behavior on the interrupt signal and notify user of shutdown. + stop() + log.Println("shutting down gracefully, press Ctrl+C again to force") - log.Println("Server exiting") + // The context is used to inform the server it has 5 seconds to finish + // the request it is currently handling + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + log.Fatal("Server forced to shutdown: ", err) + } + + log.Println("Server exiting") } diff --git a/server/api/analytic.go b/server/api/analytic.go index 2ca3a54f..f5e45a85 100644 --- a/server/api/analytic.go +++ b/server/api/analytic.go @@ -3,6 +3,7 @@ package api import ( "encoding/json" "fmt" + "github.com/0xJacky/Nginx-UI/server/tool" "github.com/shirou/gopsutil/v3/cpu" "github.com/shirou/gopsutil/v3/disk" "github.com/shirou/gopsutil/v3/host" @@ -97,3 +98,10 @@ func Analytic(c *gin.Context) { } } } + +func GetCpuUsageRecord(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "user": tool.CpuUserBuffer, + "total": tool.CpuTotalBuffer, + }) +} diff --git a/server/router/routers.go b/server/router/routers.go index 7f2885f3..c3fd2791 100644 --- a/server/router/routers.go +++ b/server/router/routers.go @@ -38,6 +38,7 @@ func InitRouter() *gin.Engine { g := g.Group("/", authRequired()) { g.GET("/analytic", api.Analytic) + g.GET("/analytic/cpu", api.GetCpuUsageRecord) g.GET("/users", api.GetUsers) g.GET("/user/:id", api.GetUser) diff --git a/server/tool/cpu_usage.go b/server/tool/cpu_usage.go new file mode 100644 index 00000000..8961e97a --- /dev/null +++ b/server/tool/cpu_usage.go @@ -0,0 +1,45 @@ +package tool + +import ( + "github.com/shirou/gopsutil/v3/cpu" + "runtime" + "time" +) + +type cpuUsage struct { + Time time.Time `json:"x"` + Usage float64 `json:"y"` +} + +var CpuUserBuffer []cpuUsage +var CpuTotalBuffer []cpuUsage + +func RecordCpuUsage() { + for { + cpuTimesBefore, _ := cpu.Times(false) + time.Sleep(1000 * time.Millisecond) + cpuTimesAfter, _ := cpu.Times(false) + threadNum := runtime.GOMAXPROCS(0) + + cpuUserUsage := (cpuTimesAfter[0].User - cpuTimesBefore[0].User) / (float64(1000*threadNum) / 1000) + cpuSystemUsage := (cpuTimesAfter[0].System - cpuTimesBefore[0].System) / (float64(1000*threadNum) / 1000) + now := time.Now() + u := cpuUsage{ + Time: now, + Usage: cpuUserUsage, + } + CpuUserBuffer = append(CpuUserBuffer, u) + s := cpuUsage{ + Time: now, + Usage: cpuUserUsage + cpuSystemUsage, + } + CpuTotalBuffer = append(CpuTotalBuffer, s) + if len(CpuUserBuffer) > 200 { + CpuUserBuffer = CpuUserBuffer[1:] + } + if len(CpuTotalBuffer) > 200 { + CpuTotalBuffer = CpuTotalBuffer[1:] + } + // time.Sleep(1 * time.Second) + } +}