Files
team-api/internal/logic/monitor/redis_monitor.go
T
2026-05-10 12:21:27 +08:00

111 lines
2.9 KiB
Go

package monitor
import (
"context"
"strings"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
// RedisPoolMetrics holds Redis connection and memory statistics.
type RedisPoolMetrics struct {
ConnectedClients int `json:"connected_clients"`
UsedMemoryMB float64 `json:"used_memory_mb"`
MaxMemoryMB float64 `json:"max_memory_mb"`
UsedMemoryPercent float64 `json:"used_memory_percent"`
TotalCommands int64 `json:"total_commands"`
InstantaneousOps int `json:"instantaneous_ops"`
KeyspaceHits int64 `json:"keyspace_hits"`
KeyspaceMisses int64 `json:"keyspace_misses"`
HitRate float64 `json:"hit_rate"`
}
// GetRedisPoolMetrics returns current Redis statistics.
func GetRedisPoolMetrics(ctx context.Context) (*RedisPoolMetrics, error) {
m := &RedisPoolMetrics{}
// Get INFO all at once
val, err := g.Redis().Do(ctx, "INFO", "all")
if err != nil {
g.Log().Warningf(ctx, "redis INFO failed: %v", err)
return m, err
}
info := val.String()
lines := strings.Split(info, "\n")
kv := make(map[string]string)
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "#") || line == "" || !strings.Contains(line, ":") {
continue
}
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
kv[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
// Clients
if v, ok := kv["connected_clients"]; ok {
m.ConnectedClients = gconv.Int(v)
}
// Memory
if v, ok := kv["used_memory"]; ok {
bytes := gconv.Int64(v)
m.UsedMemoryMB = float64(bytes) / 1024 / 1024
}
if v, ok := kv["maxmemory"]; ok {
bytes := gconv.Int64(v)
m.MaxMemoryMB = float64(bytes) / 1024 / 1024
}
if v, ok := kv["used_memory_percent"]; ok {
m.UsedMemoryPercent = gconv.Float64(strings.TrimSuffix(v, "%"))
} else if m.MaxMemoryMB > 0 {
m.UsedMemoryPercent = (m.UsedMemoryMB / m.MaxMemoryMB) * 100
}
// Stats
if v, ok := kv["total_commands_processed"]; ok {
m.TotalCommands = gconv.Int64(v)
}
if v, ok := kv["instantaneous_ops_per_sec"]; ok {
m.InstantaneousOps = gconv.Int(v)
}
// Keyspace
if v, ok := kv["keyspace_hits"]; ok {
m.KeyspaceHits = gconv.Int64(v)
}
if v, ok := kv["keyspace_misses"]; ok {
m.KeyspaceMisses = gconv.Int64(v)
}
total := m.KeyspaceHits + m.KeyspaceMisses
if total > 0 {
m.HitRate = float64(m.KeyspaceHits) / float64(total) * 100
}
return m, nil
}
// GetRedisUsedMemoryMB returns current Redis used memory in MB.
func GetRedisUsedMemoryMB(ctx context.Context) (float64, error) {
val, err := g.Redis().Do(ctx, "INFO", "memory")
if err != nil {
return 0, err
}
info := val.String()
for _, line := range strings.Split(info, "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "used_memory:") {
parts := strings.SplitN(line, ":", 2)
if len(parts) == 2 {
bytes := gconv.Int64(strings.TrimSpace(parts[1]))
return float64(bytes) / 1024 / 1024, nil
}
}
}
return 0, nil
}