mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
enhance: node analytic init
This commit is contained in:
parent
21cd91b163
commit
ca3d19a5d7
18 changed files with 301 additions and 264 deletions
|
@ -1 +1,3 @@
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-1.22.19.cjs
|
||||||
|
|
|
@ -50,5 +50,6 @@
|
||||||
"vite-plugin-html": "^3.2.0",
|
"vite-plugin-html": "^3.2.0",
|
||||||
"vite-svg-loader": "^4.0.0",
|
"vite-svg-loader": "^4.0.0",
|
||||||
"vue-tsc": "^1.6.1"
|
"vue-tsc": "^1.6.1"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@1.22.19"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"version":"1.9.9","build_id":127,"total_build":197}
|
{"version":"1.9.9","build_id":129,"total_build":199}
|
|
@ -1 +1 @@
|
||||||
{"version":"1.9.9","build_id":127,"total_build":197}
|
{"version":"1.9.9","build_id":129,"total_build":199}
|
3
main.go
3
main.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/0xJacky/Nginx-UI/server"
|
"github.com/0xJacky/Nginx-UI/server"
|
||||||
"github.com/0xJacky/Nginx-UI/server/service"
|
|
||||||
"github.com/0xJacky/Nginx-UI/server/settings"
|
"github.com/0xJacky/Nginx-UI/server/settings"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/jpillora/overseer"
|
"github.com/jpillora/overseer"
|
||||||
|
@ -21,7 +20,7 @@ func main() {
|
||||||
|
|
||||||
gin.SetMode(settings.ServerSettings.RunMode)
|
gin.SetMode(settings.ServerSettings.RunMode)
|
||||||
|
|
||||||
r, err := service.GetRuntimeInfo()
|
r, err := server.GetRuntimeInfo()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
|
|
|
@ -230,13 +230,13 @@ func GetIntroAnalytic(c *gin.Context) {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// write
|
// write
|
||||||
err = ws.WriteJSON(analytic.GetNodeAnalyticIntro())
|
err = ws.WriteJSON(analytic.GetNodeStat())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err)
|
logger.Error(err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(5 * time.Second)
|
time.Sleep(10 * time.Second)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/environment"
|
||||||
"github.com/0xJacky/Nginx-UI/server/model"
|
"github.com/0xJacky/Nginx-UI/server/model"
|
||||||
"github.com/0xJacky/Nginx-UI/server/query"
|
"github.com/0xJacky/Nginx-UI/server/query"
|
||||||
"github.com/0xJacky/Nginx-UI/server/service"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -24,7 +24,7 @@ func GetEnvironment(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetEnvironmentList(c *gin.Context) {
|
func GetEnvironmentList(c *gin.Context) {
|
||||||
data, err := service.RetrieveEnvironmentList()
|
data, err := environment.RetrieveEnvironmentList()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,7 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/analytic"
|
"github.com/0xJacky/Nginx-UI/server/internal/analytic"
|
||||||
"github.com/0xJacky/Nginx-UI/server/service"
|
"github.com/0xJacky/Nginx-UI/server/internal/upgrader"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
|
@ -18,28 +18,28 @@ func GetCurrentNode(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runtimeInfo, err := service.GetRuntimeInfo()
|
runtimeInfo, err := upgrader.GetRuntimeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuInfo, _ := cpu.Info()
|
cpuInfo, _ := cpu.Info()
|
||||||
memory, _ := getMemoryStat()
|
memory, _ := getMemoryStat()
|
||||||
ver, _ := service.GetCurrentVersion()
|
ver, _ := upgrader.GetCurrentVersion()
|
||||||
diskUsage, _ := disk.Usage(".")
|
diskUsage, _ := disk.Usage(".")
|
||||||
|
|
||||||
intro := analytic.GetNodeAnalyticIntro()
|
nodeInfo := analytic.NodeInfo{
|
||||||
|
NodeRuntimeInfo: runtimeInfo,
|
||||||
nodeInfo := service.NodeInfo{
|
CPUNum: len(cpuInfo),
|
||||||
RequestNodeSecret: c.MustGet("NodeSecret").(string),
|
MemoryTotal: memory.Total,
|
||||||
NodeRuntimeInfo: runtimeInfo,
|
DiskTotal: humanize.Bytes(diskUsage.Total),
|
||||||
CPUNum: len(cpuInfo),
|
Version: ver.Version,
|
||||||
MemoryTotal: memory.Total,
|
|
||||||
DiskTotal: humanize.Bytes(diskUsage.Total),
|
|
||||||
Version: ver.Version,
|
|
||||||
Node: intro,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, nodeInfo)
|
stat := analytic.GetNodeStat()
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, analytic.Node{
|
||||||
|
NodeInfo: nodeInfo,
|
||||||
|
NodeStat: stat,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/nginx"
|
"github.com/0xJacky/Nginx-UI/server/internal/nginx"
|
||||||
"github.com/0xJacky/Nginx-UI/server/service"
|
"github.com/0xJacky/Nginx-UI/server/internal/template"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,7 @@ func GetTemplate(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTemplateConfList(c *gin.Context) {
|
func GetTemplateConfList(c *gin.Context) {
|
||||||
configList, err := service.GetTemplateList("conf")
|
configList, err := template.GetTemplateList("conf")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
|
@ -58,7 +58,7 @@ func GetTemplateConfList(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTemplateBlockList(c *gin.Context) {
|
func GetTemplateBlockList(c *gin.Context) {
|
||||||
configList, err := service.GetTemplateList("block")
|
configList, err := template.GetTemplateList("block")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
|
@ -72,18 +72,18 @@ func GetTemplateBlockList(c *gin.Context) {
|
||||||
|
|
||||||
func GetTemplateBlock(c *gin.Context) {
|
func GetTemplateBlock(c *gin.Context) {
|
||||||
type resp struct {
|
type resp struct {
|
||||||
service.ConfigInfoItem
|
template.ConfigInfoItem
|
||||||
service.ConfigDetail
|
template.ConfigDetail
|
||||||
}
|
}
|
||||||
var bindData map[string]service.TVariable
|
var bindData map[string]template.TVariable
|
||||||
_ = c.ShouldBindJSON(&bindData)
|
_ = c.ShouldBindJSON(&bindData)
|
||||||
info := service.GetTemplateInfo("block", c.Param("name"))
|
info := template.GetTemplateInfo("block", c.Param("name"))
|
||||||
|
|
||||||
if bindData == nil {
|
if bindData == nil {
|
||||||
bindData = info.Variables
|
bindData = info.Variables
|
||||||
}
|
}
|
||||||
|
|
||||||
detail, err := service.ParseTemplate("block", c.Param("name"), bindData)
|
detail, err := template.ParseTemplate("block", c.Param("name"), bindData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -2,7 +2,7 @@ package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
||||||
"github.com/0xJacky/Nginx-UI/server/service"
|
"github.com/0xJacky/Nginx-UI/server/internal/upgrader"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -10,19 +10,19 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetRelease(c *gin.Context) {
|
func GetRelease(c *gin.Context) {
|
||||||
data, err := service.GetRelease(c.Query("channel"))
|
data, err := upgrader.GetRelease(c.Query("channel"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
runtimeInfo, err := service.GetRuntimeInfo()
|
runtimeInfo, err := upgrader.GetRuntimeInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
type resp struct {
|
type resp struct {
|
||||||
service.TRelease
|
upgrader.TRelease
|
||||||
service.RuntimeInfo
|
upgrader.RuntimeInfo
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, resp{
|
c.JSON(http.StatusOK, resp{
|
||||||
data, runtimeInfo,
|
data, runtimeInfo,
|
||||||
|
@ -30,7 +30,7 @@ func GetRelease(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCurrentVersion(c *gin.Context) {
|
func GetCurrentVersion(c *gin.Context) {
|
||||||
curVer, err := service.GetCurrentVersion()
|
curVer, err := upgrader.GetCurrentVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ErrHandler(c, err)
|
ErrHandler(c, err)
|
||||||
return
|
return
|
||||||
|
@ -70,7 +70,7 @@ func PerformCoreUpgrade(c *gin.Context) {
|
||||||
"message": "Initialing core upgrader",
|
"message": "Initialing core upgrader",
|
||||||
})
|
})
|
||||||
|
|
||||||
u, err := service.NewUpgrader(control.Channel)
|
u, err := upgrader.NewUpgrader(control.Channel)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = ws.WriteJSON(gin.H{
|
_ = ws.WriteJSON(gin.H{
|
||||||
|
|
|
@ -1,31 +1,43 @@
|
||||||
package analytic
|
package analytic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/upgrader"
|
||||||
"github.com/0xJacky/Nginx-UI/server/model"
|
"github.com/0xJacky/Nginx-UI/server/model"
|
||||||
"github.com/0xJacky/Nginx-UI/server/query"
|
|
||||||
"github.com/gorilla/websocket"
|
|
||||||
"github.com/opentracing/opentracing-go/log"
|
|
||||||
"github.com/shirou/gopsutil/v3/cpu"
|
|
||||||
"github.com/shirou/gopsutil/v3/load"
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
"github.com/shirou/gopsutil/v3/net"
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
"math"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"runtime"
|
"net/url"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Node struct {
|
type NodeInfo struct {
|
||||||
EnvironmentID int `json:"environment_id,omitempty"`
|
NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
|
||||||
Name string `json:"name,omitempty"`
|
Version string `json:"version"`
|
||||||
|
CPUNum int `json:"cpu_num"`
|
||||||
|
MemoryTotal string `json:"memory_total"`
|
||||||
|
DiskTotal string `json:"disk_total"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type NodeStat struct {
|
||||||
AvgLoad *load.AvgStat `json:"avg_load"`
|
AvgLoad *load.AvgStat `json:"avg_load"`
|
||||||
CPUPercent float64 `json:"cpu_percent"`
|
CPUPercent float64 `json:"cpu_percent"`
|
||||||
MemoryPercent float64 `json:"memory_percent"`
|
MemoryPercent float64 `json:"memory_percent"`
|
||||||
DiskPercent float64 `json:"disk_percent"`
|
DiskPercent float64 `json:"disk_percent"`
|
||||||
Network net.IOCountersStat `json:"network"`
|
Network net.IOCountersStat `json:"network"`
|
||||||
Status bool `json:"status"`
|
Status bool `json:"status"`
|
||||||
|
ResponseAt time.Time `json:"response_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node struct {
|
||||||
|
EnvironmentID int `json:"environment_id,omitempty"`
|
||||||
|
*model.Environment
|
||||||
|
NodeStat
|
||||||
|
NodeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
var mutex sync.Mutex
|
var mutex sync.Mutex
|
||||||
|
@ -38,136 +50,66 @@ func init() {
|
||||||
NodeMap = make(TNodeMap)
|
NodeMap = make(TNodeMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeAnalyticLive(env *model.Environment, errChan chan error) {
|
func GetNode(env *model.Environment) (n *Node) {
|
||||||
for {
|
if env == nil {
|
||||||
err := nodeAnalyticRecord(env)
|
logger.Error("env is nil")
|
||||||
|
return
|
||||||
if err != nil {
|
}
|
||||||
// set node offline
|
n, ok := NodeMap[env.ID]
|
||||||
if NodeMap[env.ID] != nil {
|
if !ok {
|
||||||
NodeMap[env.ID].Status = false
|
n = &Node{
|
||||||
}
|
Environment: env,
|
||||||
log.Error(err)
|
|
||||||
errChan <- err
|
|
||||||
// wait 5s then reconnect
|
|
||||||
time.Sleep(5 * time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func nodeAnalyticRecord(env *model.Environment) (err error) {
|
func InitNode(env *model.Environment) (n *Node) {
|
||||||
url, err := env.GetWebSocketURL("/api/analytic/intro")
|
n = &Node{
|
||||||
|
Environment: env,
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.JoinPath(env.URL, "/api/node")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
header := http.Header{}
|
|
||||||
|
|
||||||
header.Set("X-Node-Secret", env.Token)
|
|
||||||
|
|
||||||
c, _, err := websocket.DefaultDialer.Dial(url, header)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
client := http.Client{
|
||||||
|
Transport: &http.Transport{
|
||||||
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("GET", u, nil)
|
||||||
|
req.Header.Set("X-Node-Secret", env.Token)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer c.Close()
|
defer resp.Body.Close()
|
||||||
|
bytes, _ := io.ReadAll(resp.Body)
|
||||||
|
|
||||||
for {
|
if resp.StatusCode != 200 {
|
||||||
_, message, err := c.ReadMessage()
|
logger.Error(string(bytes))
|
||||||
if err != nil {
|
return
|
||||||
return err
|
|
||||||
}
|
|
||||||
logger.Debugf("recv: %s %s", env.Name, message)
|
|
||||||
|
|
||||||
var nodeAnalytic Node
|
|
||||||
|
|
||||||
err = json.Unmarshal(message, &nodeAnalytic)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeAnalytic.EnvironmentID = env.ID
|
|
||||||
nodeAnalytic.Name = env.Name
|
|
||||||
// set online
|
|
||||||
nodeAnalytic.Status = true
|
|
||||||
mutex.Lock()
|
|
||||||
NodeMap[env.ID] = &nodeAnalytic
|
|
||||||
mutex.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func RetrieveNodesStatus() {
|
|
||||||
NodeMap = make(TNodeMap)
|
|
||||||
|
|
||||||
env := query.Environment
|
|
||||||
|
|
||||||
envs, err := env.Find()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
errChan := make(chan error)
|
|
||||||
|
|
||||||
for _, v := range envs {
|
|
||||||
go nodeAnalyticLive(v, errChan)
|
|
||||||
}
|
|
||||||
|
|
||||||
// block at here
|
|
||||||
for err = range errChan {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetNodeAnalyticIntro() (data Node) {
|
|
||||||
memory, err := GetMemoryStat()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
loadAvg, err := load.Avg()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
diskStat, err := GetDiskStat()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
netIO, err := net.IOCounters(false)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var network net.IOCountersStat
|
|
||||||
if len(netIO) > 0 {
|
|
||||||
network = netIO[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return Node{
|
|
||||||
AvgLoad: loadAvg,
|
|
||||||
CPUPercent: math.Min((cpuUserUsage+cpuSystemUsage)*100, 100),
|
|
||||||
MemoryPercent: memory.Pressure,
|
|
||||||
DiskPercent: diskStat.Percentage,
|
|
||||||
Network: network,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Debug(string(bytes))
|
||||||
|
err = json.Unmarshal(bytes, &n.NodeInfo)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
102
server/internal/analytic/node_record.go
Normal file
102
server/internal/analytic/node_record.go
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
package analytic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/model"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/query"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/opentracing/opentracing-go/log"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RetrieveNodesStatus() {
|
||||||
|
NodeMap = make(TNodeMap)
|
||||||
|
|
||||||
|
env := query.Environment
|
||||||
|
|
||||||
|
envs, err := env.Find()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
errChan := make(chan error)
|
||||||
|
|
||||||
|
for _, v := range envs {
|
||||||
|
go nodeAnalyticLive(v, errChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// block at here
|
||||||
|
for err = range errChan {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeAnalyticLive(env *model.Environment, errChan chan error) {
|
||||||
|
for {
|
||||||
|
err := nodeAnalyticRecord(env)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// set node offline
|
||||||
|
if NodeMap[env.ID] != nil {
|
||||||
|
mutex.Lock()
|
||||||
|
NodeMap[env.ID].Status = false
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
logger.Error(err)
|
||||||
|
errChan <- err
|
||||||
|
// wait 5s then reconnect
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nodeAnalyticRecord(env *model.Environment) (err error) {
|
||||||
|
mutex.Lock()
|
||||||
|
NodeMap[env.ID] = InitNode(env)
|
||||||
|
mutex.Unlock()
|
||||||
|
|
||||||
|
u, err := env.GetWebSocketURL("/api/analytic/intro")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
header := http.Header{}
|
||||||
|
|
||||||
|
header.Set("X-Node-Secret", env.Token)
|
||||||
|
|
||||||
|
c, _, err := websocket.DefaultDialer.Dial(u, header)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
var nodeStat NodeStat
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, message, err := c.ReadMessage()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logger.Debugf("recv: %s %s", env.Name, message)
|
||||||
|
|
||||||
|
err = json.Unmarshal(message, &nodeStat)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// set online
|
||||||
|
nodeStat.Status = true
|
||||||
|
nodeStat.ResponseAt = time.Now()
|
||||||
|
|
||||||
|
mutex.Lock()
|
||||||
|
NodeMap[env.ID].NodeStat = nodeStat
|
||||||
|
mutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
61
server/internal/analytic/node_stat.go
Normal file
61
server/internal/analytic/node_stat.go
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
package analytic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
||||||
|
"github.com/shirou/gopsutil/v3/cpu"
|
||||||
|
"github.com/shirou/gopsutil/v3/load"
|
||||||
|
"github.com/shirou/gopsutil/v3/net"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetNodeStat() (data NodeStat) {
|
||||||
|
memory, err := GetMemoryStat()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
loadAvg, err := load.Avg()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
diskStat, err := GetDiskStat()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
netIO, err := net.IOCounters(false)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var network net.IOCountersStat
|
||||||
|
if len(netIO) > 0 {
|
||||||
|
network = netIO[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return NodeStat{
|
||||||
|
AvgLoad: loadAvg,
|
||||||
|
CPUPercent: math.Min((cpuUserUsage+cpuSystemUsage)*100, 100),
|
||||||
|
MemoryPercent: memory.Pressure,
|
||||||
|
DiskPercent: diskStat.Percentage,
|
||||||
|
Network: network,
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,23 @@
|
||||||
package environment
|
package environment
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/analytic"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/query"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RetrieveEnvironmentList() (envs []*analytic.Node, err error) {
|
||||||
|
envQuery := query.Environment
|
||||||
|
|
||||||
|
data, err := envQuery.Find()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range data {
|
||||||
|
t := analytic.GetNode(v)
|
||||||
|
|
||||||
|
envs = append(envs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package service
|
package template
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
|
@ -1,4 +1,4 @@
|
||||||
package service
|
package upgrader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/cert"
|
"github.com/0xJacky/Nginx-UI/server/internal/cert"
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/nginx"
|
"github.com/0xJacky/Nginx-UI/server/internal/nginx"
|
||||||
|
"github.com/0xJacky/Nginx-UI/server/internal/upgrader"
|
||||||
"github.com/0xJacky/Nginx-UI/server/model"
|
"github.com/0xJacky/Nginx-UI/server/model"
|
||||||
"github.com/0xJacky/Nginx-UI/server/query"
|
"github.com/0xJacky/Nginx-UI/server/query"
|
||||||
"github.com/0xJacky/Nginx-UI/server/router"
|
"github.com/0xJacky/Nginx-UI/server/router"
|
||||||
|
@ -18,6 +19,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func GetRuntimeInfo() (r upgrader.RuntimeInfo, err error) {
|
||||||
|
return upgrader.GetRuntimeInfo()
|
||||||
|
}
|
||||||
|
|
||||||
func Program(state overseer.State) {
|
func Program(state overseer.State) {
|
||||||
defer logger.Sync()
|
defer logger.Sync()
|
||||||
// Hack: fix wrong Content Type of .js file on some OS platforms
|
// Hack: fix wrong Content Type of .js file on some OS platforms
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"encoding/json"
|
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/analytic"
|
|
||||||
"github.com/0xJacky/Nginx-UI/server/internal/logger"
|
|
||||||
"github.com/0xJacky/Nginx-UI/server/model"
|
|
||||||
"github.com/0xJacky/Nginx-UI/server/query"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Environment struct {
|
|
||||||
*model.Environment
|
|
||||||
Status bool `json:"status"`
|
|
||||||
NodeInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func RetrieveEnvironmentList() (envs []*Environment, err error) {
|
|
||||||
envQuery := query.Environment
|
|
||||||
|
|
||||||
data, err := envQuery.Find()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range data {
|
|
||||||
t := &Environment{
|
|
||||||
Environment: v,
|
|
||||||
}
|
|
||||||
|
|
||||||
node, status := t.GetNode()
|
|
||||||
t.Status = status
|
|
||||||
t.NodeInfo = node
|
|
||||||
|
|
||||||
envs = append(envs, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type NodeInfo struct {
|
|
||||||
RequestNodeSecret string `json:"request_node_secret"`
|
|
||||||
NodeRuntimeInfo RuntimeInfo `json:"node_runtime_info"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
CPUNum int `json:"cpu_num"`
|
|
||||||
MemoryTotal string `json:"memory_total"`
|
|
||||||
DiskTotal string `json:"disk_total"`
|
|
||||||
ResponseAt time.Time `json:"response_at"`
|
|
||||||
analytic.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
func (env *Environment) GetNode() (node NodeInfo, status bool) {
|
|
||||||
u, err := url.JoinPath(env.URL, "/api/node")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
client := http.Client{
|
|
||||||
Transport: &http.Transport{
|
|
||||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
req, err := http.NewRequest("GET", u, nil)
|
|
||||||
req.Header.Set("X-Node-Secret", env.Token)
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer resp.Body.Close()
|
|
||||||
bytes, _ := io.ReadAll(resp.Body)
|
|
||||||
|
|
||||||
if resp.StatusCode != 200 {
|
|
||||||
logger.Error(string(bytes))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debug(string(bytes))
|
|
||||||
err = json.Unmarshal(bytes, &node)
|
|
||||||
if err != nil {
|
|
||||||
logger.Error(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
node.ResponseAt = time.Now()
|
|
||||||
status = true
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue