diff --git a/internal/db/models/server_bandwidth_stat_dao.go b/internal/db/models/server_bandwidth_stat_dao.go index 8599ba3b..793eb3ad 100644 --- a/internal/db/models/server_bandwidth_stat_dao.go +++ b/internal/db/models/server_bandwidth_stat_dao.go @@ -15,6 +15,7 @@ import ( "github.com/iwind/TeaGo/types" timeutil "github.com/iwind/TeaGo/utils/time" "math" + "regexp" "strings" "sync" "time" @@ -452,6 +453,75 @@ func (this *ServerBandwidthStatDAO) FindPercentileBetweenDays(tx *dbs.Tx, server return one.(*ServerBandwidthStat), nil } +// FindPercentileBetweenTimes 获取时间段内内百分位 +// timeFrom 开始时间,格式 YYYYMMDDHHII +// timeTo 结束时间,格式 YYYYMMDDHHII +func (this *ServerBandwidthStatDAO) FindPercentileBetweenTimes(tx *dbs.Tx, serverId int64, timeFrom string, timeTo string, percentile int32) (result *ServerBandwidthStat, err error) { + var reg = regexp.MustCompile(`^\d{12}$`) + if !reg.MatchString(timeFrom) { + return nil, errors.New("invalid timeFrom '" + timeFrom + "'") + } + if !reg.MatchString(timeTo) { + return nil, errors.New("invalid timeTo '" + timeTo + "'") + } + + if timeFrom > timeTo { + timeFrom, timeTo = timeTo, timeFrom + } + + if percentile <= 0 { + percentile = 95 + } + + // 如果是100%以上,则快速返回 + if percentile >= 100 { + one, err := this.Query(tx). + Table(this.partialTable(serverId)). + Attr("serverId", serverId). + Between("CONCAT(day, timeAt)", timeFrom, timeTo). + Desc("bytes"). + Find() + if err != nil || one == nil { + return nil, err + } + + return one.(*ServerBandwidthStat), nil + } + + // 总数量 + total, err := this.Query(tx). + Table(this.partialTable(serverId)). + Attr("serverId", serverId). + Between("CONCAT(day, timeAt)", timeFrom, timeTo). + Count() + if err != nil { + return nil, err + } + if total == 0 { + return nil, nil + } + + var offset int64 + + if total > 1 { + offset = int64(math.Ceil(float64(total) * float64(100-percentile) / 100)) + } + + // 查询 nth 位置 + one, err := this.Query(tx). + Table(this.partialTable(serverId)). + Attr("serverId", serverId). + Between("CONCAT(day, timeAt)", timeFrom, timeTo). + Desc("bytes"). + Offset(offset). + Find() + if err != nil || one == nil { + return nil, err + } + + return one.(*ServerBandwidthStat), nil +} + // Clean 清理过期数据 func (this *ServerBandwidthStatDAO) Clean(tx *dbs.Tx) error { var day = timeutil.Format("Ymd", time.Now().AddDate(0, 0, -100)) // 保留大约3个月的数据 diff --git a/internal/rpc/services/service_server_bandwidth_stat.go b/internal/rpc/services/service_server_bandwidth_stat.go index d1caa7ab..991c0c1c 100644 --- a/internal/rpc/services/service_server_bandwidth_stat.go +++ b/internal/rpc/services/service_server_bandwidth_stat.go @@ -10,8 +10,10 @@ import ( "github.com/TeaOSLab/EdgeAPI/internal/remotelogs" "github.com/TeaOSLab/EdgeAPI/internal/utils/regexputils" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" + "github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs" "github.com/iwind/TeaGo/dbs" "github.com/iwind/TeaGo/types" + timeutil "github.com/iwind/TeaGo/utils/time" "strings" "sync" "time" @@ -186,14 +188,45 @@ func (this *ServerBandwidthStatService) FindHourlyServerBandwidthStats(ctx conte return nil, err } + if req.Hours <= 0 { + req.Hours = 12 + } + var tx = this.NullTx() stats, err := models.SharedServerBandwidthStatDAO.FindHourlyBandwidthStats(tx, req.ServerId, req.Hours) if err != nil { return nil, err } + // percentile + var percentile = systemconfigs.DefaultBandwidthPercentile + userUIConfig, _ := models.SharedSysSettingDAO.ReadUserUIConfig(tx) + if userUIConfig != nil && userUIConfig.TrafficStats.BandwidthPercentile > 0 { + percentile = userUIConfig.TrafficStats.BandwidthPercentile + } + + var timestamp = time.Now().Unix() - int64(req.Hours)*3600 + var timeFrom = timeutil.FormatTime("YmdH00", timestamp) + var timeTo = timeutil.Format("YmdHi") + + var pbNthStat *pb.FindHourlyServerBandwidthStatsResponse_Stat + percentileStat, err := models.SharedServerBandwidthStatDAO.FindPercentileBetweenTimes(tx, req.ServerId, timeFrom, timeTo, percentile) + if err != nil { + return nil, err + } + if percentileStat != nil { + pbNthStat = &pb.FindHourlyServerBandwidthStatsResponse_Stat{ + Day: percentileStat.Day, + Hour: types.Int32(percentileStat.TimeAt[:2]), + Bytes: int64(percentileStat.Bytes), + Bits: int64(percentileStat.Bytes * 8), + } + } + return &pb.FindHourlyServerBandwidthStatsResponse{ - Stats: stats, + Stats: stats, + Percentile: percentile, + NthStat: pbNthStat, }, nil } @@ -205,13 +238,52 @@ func (this *ServerBandwidthStatService) FindDailyServerBandwidthStats(ctx contex } var tx = this.NullTx() - stats, err := models.SharedServerBandwidthStatDAO.FindDailyBandwidthStats(tx, req.ServerId, req.Days) + + if req.Days <= 0 { + req.Days = 30 + } + + var timestamp = time.Now().Unix() - int64(req.Days)*86400 + var dayFrom = timeutil.FormatTime("Ymd", timestamp) + var dayTo = timeutil.Format("Ymd") + + stats, err := models.SharedServerBandwidthStatDAO.FindBandwidthStatsBetweenDays(tx, req.ServerId, dayFrom, dayTo) if err != nil { return nil, err } + var pbStats = []*pb.FindDailyServerBandwidthStatsResponse_Stat{} + for _, stat := range stats { + pbStats = append(pbStats, &pb.FindDailyServerBandwidthStatsResponse_Stat{ + Day: stat.Day, + Bytes: stat.Bytes, + Bits: stat.Bytes * 8, + }) + } + + // percentile + var percentile = systemconfigs.DefaultBandwidthPercentile + userUIConfig, _ := models.SharedSysSettingDAO.ReadUserUIConfig(tx) + if userUIConfig != nil && userUIConfig.TrafficStats.BandwidthPercentile > 0 { + percentile = userUIConfig.TrafficStats.BandwidthPercentile + } + + var pbNthStat = &pb.FindDailyServerBandwidthStatsResponse_Stat{} + percentileStat, err := models.SharedServerBandwidthStatDAO.FindPercentileBetweenDays(tx, req.ServerId, dayFrom, dayTo, percentile) + if err != nil { + return nil, err + } + if percentileStat != nil { + pbNthStat = &pb.FindDailyServerBandwidthStatsResponse_Stat{ + Day: percentileStat.Day, + Bytes: int64(percentileStat.Bytes), + Bits: int64(percentileStat.Bytes * 8), + } + } return &pb.FindDailyServerBandwidthStatsResponse{ - Stats: stats, + Stats: pbStats, + Percentile: percentile, + NthStat: pbNthStat, }, nil } diff --git a/internal/rpc/services/service_server_stat_board.go b/internal/rpc/services/service_server_stat_board.go index d2f388b8..1f8ec8ac 100644 --- a/internal/rpc/services/service_server_stat_board.go +++ b/internal/rpc/services/service_server_stat_board.go @@ -14,6 +14,7 @@ import ( "github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs" "github.com/TeaOSLab/EdgeCommon/pkg/rpc/pb" "github.com/TeaOSLab/EdgeCommon/pkg/serverconfigs" + "github.com/TeaOSLab/EdgeCommon/pkg/systemconfigs" timeutil "github.com/iwind/TeaGo/utils/time" "time" ) @@ -465,7 +466,16 @@ func (this *ServerStatBoardService) ComposeServerStatBoard(ctx context.Context, { var bandwidthMinutes = utils.RangeMinutes(time.Now(), 12, 5) var bandwidthStatMap = map[string]*pb.ServerBandwidthStat{} + var timeFrom = "" + var timeTo = "" for _, r := range utils.GroupMinuteRanges(bandwidthMinutes) { + if len(timeFrom) == 0 || timeFrom > r.Day+r.MinuteFrom { + timeFrom = r.Day + r.MinuteFrom + } + if len(timeTo) == 0 || timeTo < r.Day+r.MinuteTo { + timeTo = r.Day + r.MinuteTo + } + bandwidthStats, err := models.SharedServerBandwidthStatDAO.FindServerStats(tx, req.ServerId, r.Day, r.MinuteFrom, r.MinuteTo) if err != nil { return nil, err @@ -499,6 +509,29 @@ func (this *ServerStatBoardService) ComposeServerStatBoard(ctx context.Context, } } result.MinutelyBandwidthStats = pbBandwidthStats + + // percentile + if len(timeFrom) > 0 && len(timeTo) > 0 { + var percentile = systemconfigs.DefaultBandwidthPercentile + userUIConfig, _ := models.SharedSysSettingDAO.ReadUserUIConfig(tx) + if userUIConfig != nil && userUIConfig.TrafficStats.BandwidthPercentile > 0 { + percentile = userUIConfig.TrafficStats.BandwidthPercentile + } + result.BandwidthPercentile = percentile + + percentileStat, err := models.SharedServerBandwidthStatDAO.FindPercentileBetweenTimes(tx, req.ServerId, timeFrom, timeTo, percentile) + if err != nil { + return nil, err + } + if percentileStat != nil { + result.MinutelyNthBandwidthStat = &pb.ServerBandwidthStat{ + Day: percentileStat.Day, + TimeAt: percentileStat.TimeAt, + Bytes: int64(percentileStat.Bytes), + Bits: int64(percentileStat.Bytes * 8), + } + } + } } // 按日流量统计