diff --git a/src/modules/monitor/controller/monitor.go b/src/modules/monitor/controller/monitor.go new file mode 100644 index 00000000..a69efe71 --- /dev/null +++ b/src/modules/monitor/controller/monitor.go @@ -0,0 +1,57 @@ +package controller + +import ( + "ems.agt/src/framework/vo/result" + "ems.agt/src/modules/monitor/service" + + "github.com/gin-gonic/gin" +) + +// 实例化控制层 MonitorInfoController 结构体 +var NewMonitor = &MonitorController{ + monitorService: service.NewMonitorImpl, +} + +// 服务器资源监控信息 +// +// PATH /monitor +type MonitorController struct { + // 服务器系统相关信息服务 + monitorService service.IMonitor +} + +// 资源监控信息加载 +// +// GET /load +func (s *MonitorController) Load(c *gin.Context) { + var querys struct { + // 数据类型all/cpu/memory/io/network + Type string `form:"type" binding:"required,oneof=all cpu memory io network"` + // 开始时间 + StartTime int64 `form:"startTime" binding:"required"` + // 结束时间 + EndTime int64 `form:"endTime" binding:"required"` + // 网元类型 + NeType string `form:"neType"` + // 网元ID + NeID string `form:"neId"` + // 名称,networ和iok时有效 + Name string `form:"name"` + } + err := c.ShouldBindQuery(&querys) + if err != nil { + c.JSON(400, result.CodeMsg(400, "参数错误")) + return + } + + // 查询数据 + data := s.monitorService.SelectMonitorInfo(map[string]any{ + "type": querys.Type, + "startTime": querys.StartTime, + "endTime": querys.EndTime, + "neType": querys.NeType, + "neId": querys.NeID, + "name": querys.Name, + }) + c.JSON(200, result.OkData(data)) +} diff --git a/src/modules/monitor/model/monitor_base.go b/src/modules/monitor/model/monitor_base.go new file mode 100644 index 00000000..7669afa2 --- /dev/null +++ b/src/modules/monitor/model/monitor_base.go @@ -0,0 +1,29 @@ +package model + +// MonitorBase 监控_基本信息 monitor_base +type MonitorBase struct { + // id + ID int64 `json:"id" gorm:"primaryKey"` + // 创建时间 + CreateTime int64 `json:"createTime"` + // cpu使用率 + CPU float64 `json:"cpu"` + // cpu平均使用率 + LoadUsage float64 `json:"loadUsage"` + // cpu使用1分钟 + CPULoad1 float64 `json:"cpuLoad1"` + // cpu使用5分钟 + CPULoad5 float64 `json:"cpuLoad5"` + // cpu使用15分钟 + CPULoad15 float64 `json:"cpuLoad15"` + // 内存使用率 + Memory float64 `json:"memory"` + // 网元ID + NeType string `json:"neType"` + // 网元类型 + NeID string `json:"neId"` +} + +func (MonitorBase) TableName() string { + return "monitor_base" +} diff --git a/src/modules/monitor/model/monitor_io.go b/src/modules/monitor/model/monitor_io.go new file mode 100644 index 00000000..cc52b728 --- /dev/null +++ b/src/modules/monitor/model/monitor_io.go @@ -0,0 +1,27 @@ +package model + +// MonitorIO 监控_磁盘IO monitor_io +type MonitorIO struct { + // id + ID int64 `json:"id" gorm:"primaryKey"` + // 创建时间 + CreateTime int64 `json:"createTime"` + // 磁盘名 + Name string `json:"name"` + // 读取K + Read int64 `json:"read"` + // 写入K + Write int64 `json:"write"` + // 次数 + Count int64 `json:"count"` + // 耗时 + Time int64 `json:"time"` + // 网元ID + NeType string `json:"neType"` + // 网元类型 + NeID string `json:"neId"` +} + +func (MonitorIO) TableName() string { + return "monitor_io" +} diff --git a/src/modules/monitor/model/monitor_network.go b/src/modules/monitor/model/monitor_network.go new file mode 100644 index 00000000..b5058805 --- /dev/null +++ b/src/modules/monitor/model/monitor_network.go @@ -0,0 +1,23 @@ +package model + +// MonitorNetwork 监控_网络IO monitor_network +type MonitorNetwork struct { + // id + ID int64 `json:"id" gorm:"primaryKey"` + // 创建时间 + CreateTime int64 `json:"createTime"` + // 网卡名 + Name string `json:"name"` + // 上行 + Up float64 `json:"up"` + // 下行 + Down float64 `json:"down"` + // 网元ID + NeType string `json:"neType"` + // 网元类型 + NeID string `json:"neId"` +} + +func (MonitorNetwork) TableName() string { + return "monitor_network" +} diff --git a/src/modules/monitor/monitor.go b/src/modules/monitor/monitor.go index 549de18a..2a279bc3 100644 --- a/src/modules/monitor/monitor.go +++ b/src/modules/monitor/monitor.go @@ -19,6 +19,15 @@ func Setup(router *gin.Engine) { // 启动时需要的初始参数 InitLoad() + // 服务器资源监控信息 + monitorGroup := router.Group("/monitor") + { + monitorGroup.GET("/load", + // middleware.PreAuthorize(nil), + controller.NewMonitor.Load, + ) + } + // 服务器服务信息 router.GET("/monitor/system-info", middleware.PreAuthorize(map[string][]string{"hasPerms": {"monitor:system:info"}}), diff --git a/src/modules/monitor/repository/monitor.go b/src/modules/monitor/repository/monitor.go new file mode 100644 index 00000000..a293efed --- /dev/null +++ b/src/modules/monitor/repository/monitor.go @@ -0,0 +1,33 @@ +package repository + +import "ems.agt/src/modules/monitor/model" + +// IMonitor 监控服务资源相关信息 数据接口 +type IMonitor interface { + // CreateMonitorBase 创建监控_基本信息 + CreateMonitorBase(m model.MonitorBase) error + + // DelMonitorBase 删除监控_基本信息 + DelMonitorBase(ltTime int64) error + + // SelectMonitorBase 查询监控_基本信息 + SelectMonitorBase(query map[string]any) []model.MonitorBase + + // BatchCreateMonitorIO 批量创建监控_IO + BatchCreateMonitorIO(ioList []model.MonitorIO) error + + // DelMonitorIO 删除监控_IO + DelMonitorIO(ltTime int64) error + + // SelectMonitorIO 查询监控_IO + SelectMonitorIO(query map[string]any) []model.MonitorIO + + // BatchCreateMonitorNet 批量创建监控_网络 + BatchCreateMonitorNet(netList []model.MonitorNetwork) error + + // DelMonitorNet 删除监控_网络 + DelMonitorNet(ltTime int64) error + + // SelectMonitorNetwork 查询监控_网络 + SelectMonitorNetwork(query map[string]any) []model.MonitorNetwork +} diff --git a/src/modules/monitor/repository/monitor.impl.go b/src/modules/monitor/repository/monitor.impl.go new file mode 100644 index 00000000..c113be63 --- /dev/null +++ b/src/modules/monitor/repository/monitor.impl.go @@ -0,0 +1,103 @@ +package repository + +import ( + "ems.agt/src/framework/datasource" + "ems.agt/src/framework/logger" + "ems.agt/src/modules/monitor/model" + "gorm.io/gorm" +) + +// 实例化数据层 MonitorImpl 结构体 +var NewMonitorImpl = &MonitorImpl{ + db: datasource.DefaultDB, +} + +// MonitorImpl 监控服务资源相关信息 数据层处理 +type MonitorImpl struct { + // 数据库实例 + db func() *gorm.DB +} + +// CreateMonitorBase 创建监控_基本信息 +func (r *MonitorImpl) CreateMonitorBase(m model.MonitorBase) error { + return r.db().Create(&m).Error +} + +// DelMonitorBase 删除监控_基本信息 +func (r *MonitorImpl) DelMonitorBase(ltTime int64) error { + return r.db().Where("create_time < ?", ltTime).Delete(&model.MonitorBase{}).Error +} + +// SelectMonitorBase 查询监控_基本信息 +func (r *MonitorImpl) SelectMonitorBase(query map[string]any) []model.MonitorBase { + var bases []model.MonitorBase + dbConn := r.db() + if query["neType"] != "" && query["neId"] != "" { + dbConn = dbConn.Where("ne_type = ? and ne_id = ?", query["neType"], query["neId"]) + } + dbConn = dbConn.Where("create_time >= ? and create_time <= ?", query["startTime"], query["endTime"]) + err := dbConn.Order("create_time desc").Find(&bases).Error + if err != nil { + logger.Errorf("SelectMonitorBase %v", err) + return bases + } + return bases +} + +// BatchCreateMonitorIO 批量创建监控_IO +func (r *MonitorImpl) BatchCreateMonitorIO(ioList []model.MonitorIO) error { + return r.db().CreateInBatches(ioList, len(ioList)).Error +} + +// DelMonitorIO 删除监控_IO +func (r *MonitorImpl) DelMonitorIO(ltTime int64) error { + return r.db().Where("create_time < ?", ltTime).Delete(&model.MonitorIO{}).Error +} + +// SelectMonitorIO 查询监控_IO +func (r *MonitorImpl) SelectMonitorIO(query map[string]any) []model.MonitorIO { + var ios []model.MonitorIO + dbConn := r.db() + if query["name"] != "" { + dbConn = dbConn.Where("name = ?", query["name"]) + } + if query["neType"] != "" && query["neId"] != "" { + dbConn = dbConn.Where("ne_type = ? and ne_id = ?", query["neType"], query["neId"]) + } + dbConn = dbConn.Where("create_time >= ? and create_time <= ?", query["startTime"], query["endTime"]) + err := dbConn.Order("create_time desc").Find(&ios).Error + if err != nil { + logger.Errorf("SelectMonitorIO %v", err) + return ios + } + return ios +} + +// BatchCreateMonitorNet 批量创建监控_网络 +func (r *MonitorImpl) BatchCreateMonitorNet(netList []model.MonitorNetwork) error { + return r.db().CreateInBatches(netList, len(netList)).Error +} + +// DelMonitorNet 删除监控_网络 +func (r *MonitorImpl) DelMonitorNet(ltTime int64) error { + return r.db().Where("create_time < ?", ltTime).Delete(&model.MonitorNetwork{}).Error +} + +// SelectMonitorNetwork 查询监控_网络 +func (r *MonitorImpl) SelectMonitorNetwork(query map[string]any) []model.MonitorNetwork { + var networks []model.MonitorNetwork + dbConn := r.db() + if query["name"] != "" { + dbConn = dbConn.Where("name = ?", query["name"]) + } + if query["neType"] != "" && query["neId"] != "" { + dbConn = dbConn.Where("ne_type = ? and ne_id = ?", query["neType"], query["neId"]) + } + dbConn = dbConn.Where("create_time >= ? and create_time <= ?", query["startTime"], query["endTime"]) + err := dbConn.Order("create_time desc").Find(&networks).Error + if err != nil { + logger.Errorf("SelectMonitorNetwork %v", err) + return networks + } + return networks +} diff --git a/src/modules/monitor/service/monitor.go b/src/modules/monitor/service/monitor.go new file mode 100644 index 00000000..0ed14547 --- /dev/null +++ b/src/modules/monitor/service/monitor.go @@ -0,0 +1,14 @@ +package service + +// IMonitor 服务器系统相关信息 服务层接口 +type IMonitor interface { + // RunMonitor 执行资源监控 + RunMonitor() + + // RunMonitorDataCancel 启动资源监控数据存储io/network通道 移除之前的chan上下文后在设置新的均值 + // interval 采集的平均值(分钟) + RunMonitorDataCancel(removeBefore bool, interval float64) + + // SelectMonitorInfo 查询监控资源信息 + SelectMonitorInfo(query map[string]any) map[string]MonitorData +} diff --git a/src/modules/monitor/service/monitor.impl.go b/src/modules/monitor/service/monitor.impl.go new file mode 100644 index 00000000..b391745d --- /dev/null +++ b/src/modules/monitor/service/monitor.impl.go @@ -0,0 +1,287 @@ +package service + +import ( + "context" + "strconv" + "time" + + "ems.agt/src/framework/logger" + "ems.agt/src/modules/monitor/model" + "ems.agt/src/modules/monitor/repository" + systemService "ems.agt/src/modules/system/service" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/load" + "github.com/shirou/gopsutil/net" + "github.com/shirou/gopsutil/v3/mem" +) + +// 实例化服务层 MonitorImpl 结构体 +var NewMonitorImpl = &MonitorImpl{ + sysConfigService: systemService.NewSysConfigImpl, + monitorRepository: repository.NewMonitorImpl, + diskIO: make(chan []disk.IOCountersStat, 2), + netIO: make(chan []net.IOCountersStat, 2), +} + +// MonitorImpl 服务器系统相关信息 服务层处理 +type MonitorImpl struct { + // 参数配置服务 + sysConfigService systemService.ISysConfig + // 监控服务资源数据信息 + monitorRepository repository.IMonitor + // 磁盘网络IO 数据通道 + diskIO chan ([]disk.IOCountersStat) + netIO chan ([]net.IOCountersStat) +} + +// RunMonitor 执行资源监控 +func (s *MonitorImpl) RunMonitor() { + var itemModel model.MonitorBase + itemModel.CreateTime = time.Now().UnixMilli() + totalPercent, _ := cpu.Percent(3*time.Second, false) + if len(totalPercent) == 1 { + itemModel.CPU = totalPercent[0] + } + cpuCount, _ := cpu.Counts(false) + + loadInfo, _ := load.Avg() + itemModel.CPULoad1 = loadInfo.Load1 + itemModel.CPULoad5 = loadInfo.Load5 + itemModel.CPULoad15 = loadInfo.Load15 + itemModel.LoadUsage = loadInfo.Load1 / (float64(cpuCount*2) * 0.75) * 100 + + memoryInfo, _ := mem.VirtualMemory() + itemModel.Memory = memoryInfo.UsedPercent + if err := s.monitorRepository.CreateMonitorBase(itemModel); err != nil { + logger.Errorf("CreateMonitorBase err: %v", err) + } + + // 将当前资源发送到chan中处理保存 + s.loadDiskIO() + s.loadNetIO() + + // 监控系统资源-保留天数 + storeDays := s.sysConfigService.SelectConfigValueByKey("monitor.sysResource.storeDays") + if storeDays != "" { + storeDays, _ := strconv.Atoi(storeDays) + ltTime := time.Now().AddDate(0, 0, -storeDays).UnixMilli() + _ = s.monitorRepository.DelMonitorBase(ltTime) + _ = s.monitorRepository.DelMonitorIO(ltTime) + _ = s.monitorRepository.DelMonitorNet(ltTime) + } +} + +func (s *MonitorImpl) loadDiskIO() { + ioStat, _ := disk.IOCounters() + var diskIOList []disk.IOCountersStat + for _, io := range ioStat { + diskIOList = append(diskIOList, io) + } + s.diskIO <- diskIOList +} + +func (s *MonitorImpl) loadNetIO() { + netStat, _ := net.IOCounters(true) + netStatAll, _ := net.IOCounters(false) + var netList []net.IOCountersStat + netList = append(netList, netStat...) + netList = append(netList, netStatAll...) + s.netIO <- netList +} + +// monitorCancel 监控搜集IO/Network上下文 +var monitorCancel context.CancelFunc + +// RunMonitorDataCancel 启动资源监控数据存储io/network通道 移除之前的chan上下文后在设置新的均值 +// interval 采集的平均值(分钟) +func (s *MonitorImpl) RunMonitorDataCancel(removeBefore bool, interval float64) { + // 是否取消之前的 + if removeBefore { + monitorCancel() + } + + // 上下文控制 + ctx, cancel := context.WithCancel(context.Background()) + monitorCancel = cancel + + // chanl 通道进行存储数据 + go s.saveIODataToDB(ctx, interval) + go s.saveNetDataToDB(ctx, interval) +} + +func (s *MonitorImpl) saveIODataToDB(ctx context.Context, interval float64) { + defer close(s.diskIO) + for { + select { + case <-ctx.Done(): + return + case ioStat := <-s.diskIO: + select { + case <-ctx.Done(): + return + case ioStat2 := <-s.diskIO: + var ioList []model.MonitorIO + timeMilli := time.Now().UnixMilli() + for _, io2 := range ioStat2 { + for _, io1 := range ioStat { + if io2.Name == io1.Name { + var itemIO model.MonitorIO + itemIO.CreateTime = timeMilli + itemIO.Name = io1.Name + if io2.ReadBytes != 0 && io1.ReadBytes != 0 && io2.ReadBytes > io1.ReadBytes { + itemIO.Read = int64(float64(io2.ReadBytes-io1.ReadBytes) / interval / 60) + } + if io2.WriteBytes != 0 && io1.WriteBytes != 0 && io2.WriteBytes > io1.WriteBytes { + itemIO.Write = int64(float64(io2.WriteBytes-io1.WriteBytes) / interval / 60) + } + + if io2.ReadCount != 0 && io1.ReadCount != 0 && io2.ReadCount > io1.ReadCount { + itemIO.Count = int64(float64(io2.ReadCount-io1.ReadCount) / interval / 60) + } + writeCount := int64(0) + if io2.WriteCount != 0 && io1.WriteCount != 0 && io2.WriteCount > io1.WriteCount { + writeCount = int64(float64(io2.WriteCount-io1.WriteCount) / interval * 60) + } + if writeCount > itemIO.Count { + itemIO.Count = writeCount + } + + if io2.ReadTime != 0 && io1.ReadTime != 0 && io2.ReadTime > io1.ReadTime { + itemIO.Time = int64(float64(io2.ReadTime-io1.ReadTime) / interval / 60) + } + writeTime := int64(0) + if io2.WriteTime != 0 && io1.WriteTime != 0 && io2.WriteTime > io1.WriteTime { + writeTime = int64(float64(io2.WriteTime-io1.WriteTime) / interval / 60) + } + if writeTime > itemIO.Time { + itemIO.Time = writeTime + } + ioList = append(ioList, itemIO) + break + } + } + } + if err := s.monitorRepository.BatchCreateMonitorIO(ioList); err != nil { + logger.Errorf("BatchCreateMonitorIO err: %v", err) + } + s.diskIO <- ioStat2 + } + } + } +} + +func (s *MonitorImpl) saveNetDataToDB(ctx context.Context, interval float64) { + defer close(s.netIO) + for { + select { + case <-ctx.Done(): + return + case netStat := <-s.netIO: + select { + case <-ctx.Done(): + return + case netStat2 := <-s.netIO: + var netList []model.MonitorNetwork + timeMilli := time.Now().UnixMilli() + for _, net2 := range netStat2 { + for _, net1 := range netStat { + if net2.Name == net1.Name { + var itemNet model.MonitorNetwork + itemNet.CreateTime = timeMilli + itemNet.Name = net1.Name + + if net2.BytesSent != 0 && net1.BytesSent != 0 && net2.BytesSent > net1.BytesSent { + itemNet.Up = float64(net2.BytesSent-net1.BytesSent) / 1024 / interval / 60 + } + if net2.BytesRecv != 0 && net1.BytesRecv != 0 && net2.BytesRecv > net1.BytesRecv { + itemNet.Down = float64(net2.BytesRecv-net1.BytesRecv) / 1024 / interval / 60 + } + netList = append(netList, itemNet) + break + } + } + } + + if err := s.monitorRepository.BatchCreateMonitorNet(netList); err != nil { + logger.Errorf("BatchCreateMonitorNet err: %v", err) + } + s.netIO <- netStat2 + } + } + } +} + +// MonitorData 监控资源信息 +type MonitorData struct { + Date []int64 `json:"date"` + Value []any `json:"value"` +} + +// SelectMonitorInfo 查询监控资源信息 +func (s *MonitorImpl) SelectMonitorInfo(query map[string]any) map[string]MonitorData { + infoType := query["type"] + startTimeMilli := query["startTime"] + endTimeMilli := query["endTime"] + neType := query["neType"] + neId := query["neId"] + name := query["name"] + + // 返回数据 + backdatas := map[string]MonitorData{} + + // 基本信息 + if infoType == "all" || infoType == "cpu" || infoType == "memory" { + rows := s.monitorRepository.SelectMonitorBase(map[string]any{ + "startTime": startTimeMilli, + "endTime": endTimeMilli, + "neType": neType, + "neId": neId, + }) + // 组装数据 + var itemData MonitorData + for _, base := range rows { + itemData.Date = append(itemData.Date, base.CreateTime) + itemData.Value = append(itemData.Value, base) + } + backdatas["base"] = itemData + } + + // 磁盘IO + if infoType == "all" || infoType == "io" { + rows := s.monitorRepository.SelectMonitorIO(map[string]any{ + "startTime": startTimeMilli, + "endTime": endTimeMilli, + "neType": neType, + "neId": neId, + "name": name, + }) + // 组装数据 + var itemData MonitorData + for _, base := range rows { + itemData.Date = append(itemData.Date, base.CreateTime) + itemData.Value = append(itemData.Value, base) + } + backdatas["io"] = itemData + } + + // 网络 + if infoType == "all" || infoType == "network" { + rows := s.monitorRepository.SelectMonitorNetwork(map[string]any{ + "startTime": startTimeMilli, + "endTime": endTimeMilli, + "neType": neType, + "neId": neId, + "name": name, + }) + // 组装数据 + var itemData MonitorData + for _, base := range rows { + itemData.Date = append(itemData.Date, base.CreateTime) + itemData.Value = append(itemData.Value, base) + } + backdatas["network"] = itemData + } + + return backdatas +}