mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 14:26:36 +08:00
226 lines
6.5 KiB
Go
226 lines
6.5 KiB
Go
package service
|
|
|
|
import (
|
|
"openflare/model"
|
|
"sort"
|
|
"time"
|
|
)
|
|
|
|
const observabilityTrendBuckets = 24
|
|
|
|
type TrafficTrendPoint struct {
|
|
BucketStartedAt time.Time `json:"bucket_started_at"`
|
|
RequestCount int64 `json:"request_count"`
|
|
ErrorCount int64 `json:"error_count"`
|
|
UniqueVisitorCount int64 `json:"unique_visitor_count"`
|
|
}
|
|
|
|
type CapacityTrendPoint struct {
|
|
BucketStartedAt time.Time `json:"bucket_started_at"`
|
|
AverageCPUUsagePercent float64 `json:"average_cpu_usage_percent"`
|
|
AverageMemoryUsagePercent float64 `json:"average_memory_usage_percent"`
|
|
ReportedNodes int `json:"reported_nodes"`
|
|
}
|
|
|
|
type NetworkTrendPoint struct {
|
|
BucketStartedAt time.Time `json:"bucket_started_at"`
|
|
NetworkRxBytes int64 `json:"network_rx_bytes"`
|
|
NetworkTxBytes int64 `json:"network_tx_bytes"`
|
|
OpenrestyRxBytes int64 `json:"openresty_rx_bytes"`
|
|
OpenrestyTxBytes int64 `json:"openresty_tx_bytes"`
|
|
ReportedNodes int `json:"reported_nodes"`
|
|
}
|
|
|
|
type DiskIOTrendPoint struct {
|
|
BucketStartedAt time.Time `json:"bucket_started_at"`
|
|
DiskReadBytes int64 `json:"disk_read_bytes"`
|
|
DiskWriteBytes int64 `json:"disk_write_bytes"`
|
|
ReportedNodes int `json:"reported_nodes"`
|
|
}
|
|
|
|
type capacityTrendAccumulator struct {
|
|
cpuSum float64
|
|
cpuCount int
|
|
memSum float64
|
|
memCount int
|
|
nodes map[string]struct{}
|
|
}
|
|
|
|
type snapshotTrendAccumulator struct {
|
|
nodes map[string]struct{}
|
|
}
|
|
|
|
func buildTrafficTrendPoints(now time.Time, reports []*model.NodeRequestReport) []TrafficTrendPoint {
|
|
start := trendWindowStart(now)
|
|
points := make([]TrafficTrendPoint, observabilityTrendBuckets)
|
|
for index := range points {
|
|
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
|
|
}
|
|
|
|
for _, report := range reports {
|
|
index, ok := trendBucketIndex(report.WindowEndedAt, start)
|
|
if !ok {
|
|
continue
|
|
}
|
|
points[index].RequestCount += report.RequestCount
|
|
points[index].ErrorCount += report.ErrorCount
|
|
points[index].UniqueVisitorCount += report.UniqueVisitorCount
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
func buildCapacityTrendPoints(now time.Time, snapshots []*model.NodeMetricSnapshot) []CapacityTrendPoint {
|
|
start := trendWindowStart(now)
|
|
points := make([]CapacityTrendPoint, observabilityTrendBuckets)
|
|
accumulators := make([]capacityTrendAccumulator, observabilityTrendBuckets)
|
|
for index := range points {
|
|
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
|
|
accumulators[index].nodes = make(map[string]struct{})
|
|
}
|
|
|
|
for _, snapshot := range snapshots {
|
|
index, ok := trendBucketIndex(snapshot.CapturedAt, start)
|
|
if !ok {
|
|
continue
|
|
}
|
|
if snapshot.CPUUsagePercent > 0 {
|
|
accumulators[index].cpuSum += snapshot.CPUUsagePercent
|
|
accumulators[index].cpuCount++
|
|
}
|
|
if memoryUsage := percentage(snapshot.MemoryUsedBytes, snapshot.MemoryTotalBytes); memoryUsage > 0 {
|
|
accumulators[index].memSum += memoryUsage
|
|
accumulators[index].memCount++
|
|
}
|
|
if snapshot.NodeID != "" {
|
|
accumulators[index].nodes[snapshot.NodeID] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for index := range points {
|
|
if accumulators[index].cpuCount > 0 {
|
|
points[index].AverageCPUUsagePercent = accumulators[index].cpuSum / float64(accumulators[index].cpuCount)
|
|
}
|
|
if accumulators[index].memCount > 0 {
|
|
points[index].AverageMemoryUsagePercent = accumulators[index].memSum / float64(accumulators[index].memCount)
|
|
}
|
|
points[index].ReportedNodes = len(accumulators[index].nodes)
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
func buildNetworkTrendPoints(now time.Time, snapshots []*model.NodeMetricSnapshot) []NetworkTrendPoint {
|
|
start := trendWindowStart(now)
|
|
points := make([]NetworkTrendPoint, observabilityTrendBuckets)
|
|
accumulators := make([]snapshotTrendAccumulator, observabilityTrendBuckets)
|
|
for index := range points {
|
|
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
|
|
accumulators[index].nodes = make(map[string]struct{})
|
|
}
|
|
|
|
for _, snapshot := range snapshots {
|
|
index, ok := trendBucketIndex(snapshot.CapturedAt, start)
|
|
if !ok {
|
|
continue
|
|
}
|
|
points[index].NetworkRxBytes += snapshot.NetworkRxBytes
|
|
points[index].NetworkTxBytes += snapshot.NetworkTxBytes
|
|
points[index].OpenrestyRxBytes += snapshot.OpenrestyRxBytes
|
|
points[index].OpenrestyTxBytes += snapshot.OpenrestyTxBytes
|
|
if snapshot.NodeID != "" {
|
|
accumulators[index].nodes[snapshot.NodeID] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for index := range points {
|
|
points[index].ReportedNodes = len(accumulators[index].nodes)
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
func buildDiskIOTrendPoints(now time.Time, snapshots []*model.NodeMetricSnapshot) []DiskIOTrendPoint {
|
|
start := trendWindowStart(now)
|
|
points := make([]DiskIOTrendPoint, observabilityTrendBuckets)
|
|
accumulators := make([]snapshotTrendAccumulator, observabilityTrendBuckets)
|
|
for index := range points {
|
|
points[index].BucketStartedAt = start.Add(time.Duration(index) * time.Hour)
|
|
accumulators[index].nodes = make(map[string]struct{})
|
|
}
|
|
|
|
sort.Slice(snapshots, func(i int, j int) bool {
|
|
if snapshots[i].CapturedAt.Equal(snapshots[j].CapturedAt) {
|
|
return snapshots[i].NodeID < snapshots[j].NodeID
|
|
}
|
|
return snapshots[i].CapturedAt.Before(snapshots[j].CapturedAt)
|
|
})
|
|
|
|
type diskCounterState struct {
|
|
read int64
|
|
write int64
|
|
seen bool
|
|
}
|
|
|
|
previousByNode := make(map[string]diskCounterState, len(snapshots))
|
|
|
|
for _, snapshot := range snapshots {
|
|
nodeKey := snapshot.NodeID
|
|
if nodeKey == "" {
|
|
nodeKey = "__unknown__"
|
|
}
|
|
|
|
previous := previousByNode[nodeKey]
|
|
previousByNode[nodeKey] = diskCounterState{
|
|
read: snapshot.DiskReadBytes,
|
|
write: snapshot.DiskWriteBytes,
|
|
seen: true,
|
|
}
|
|
if !previous.seen {
|
|
continue
|
|
}
|
|
|
|
index, ok := trendBucketIndex(snapshot.CapturedAt, start)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
readDelta := snapshot.DiskReadBytes - previous.read
|
|
writeDelta := snapshot.DiskWriteBytes - previous.write
|
|
if readDelta < 0 {
|
|
readDelta = 0
|
|
}
|
|
if writeDelta < 0 {
|
|
writeDelta = 0
|
|
}
|
|
|
|
points[index].DiskReadBytes += readDelta
|
|
points[index].DiskWriteBytes += writeDelta
|
|
if snapshot.NodeID != "" {
|
|
accumulators[index].nodes[snapshot.NodeID] = struct{}{}
|
|
}
|
|
}
|
|
|
|
for index := range points {
|
|
points[index].ReportedNodes = len(accumulators[index].nodes)
|
|
}
|
|
|
|
return points
|
|
}
|
|
|
|
func trendWindowStart(now time.Time) time.Time {
|
|
return now.Truncate(time.Hour).Add(-(observabilityTrendBuckets - 1) * time.Hour)
|
|
}
|
|
|
|
func trendBucketIndex(timestamp time.Time, start time.Time) (int, bool) {
|
|
if timestamp.Before(start) {
|
|
return 0, false
|
|
}
|
|
delta := timestamp.Sub(start)
|
|
index := int(delta / time.Hour)
|
|
if index < 0 || index >= observabilityTrendBuckets {
|
|
return 0, false
|
|
}
|
|
return index, true
|
|
}
|