Files
pika/pkg/agent/collector/manager.go
T
yumusbandClaude Opus 4.6 ab22f9bd57 fix: 修复指标采集空数据时误报0%的问题
当磁盘或网卡采集不到有效数据时,空列表会被写入缓存导致
磁盘占用率和网络流量显示为0%,与真实值混淆。

修复方案:
- Agent端:空列表不发送,与GPU/温度采集器保持一致
- Server端:空数据时跳过缓存更新,保留上一次有效值

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-14 20:30:54 +08:00

206 lines
6.2 KiB
Go

package collector
import (
"time"
"github.com/dushixiang/pika/internal/protocol"
"github.com/dushixiang/pika/pkg/agent/config"
)
// WebSocketWriter 定义 WebSocket 写入接口
type WebSocketWriter interface {
WriteJSON(v interface{}) error
}
// Manager 采集器管理器
type Manager struct {
cpuCollector *CPUCollector
memoryCollector *MemoryCollector
diskCollector *DiskCollector
diskIOCollector *DiskIOCollector
networkCollector *NetworkCollector
networkConnectionCollector *NetworkConnectionCollector
hostCollector *HostCollector
temperatureCollector *TemperatureCollector
gpuCollector *GPUCollector
monitorCollector *MonitorCollector
ddnsCollector *DDNSCollector
}
// NewManager 创建采集器管理器
func NewManager(cfg *config.Config) *Manager {
return &Manager{
cpuCollector: NewCPUCollector(),
memoryCollector: NewMemoryCollector(),
diskCollector: NewDiskCollector(cfg),
diskIOCollector: NewDiskIOCollector(),
networkCollector: NewNetworkCollector(cfg),
networkConnectionCollector: NewNetworkConnectionCollector(),
hostCollector: NewHostCollector(),
temperatureCollector: NewTemperatureCollector(),
gpuCollector: NewGPUCollector(),
monitorCollector: NewMonitorCollector(),
ddnsCollector: nil, // DDNS 采集器需要配置后才能初始化
}
}
// CollectAndSendCPU 采集并发送 CPU 指标
func (m *Manager) CollectAndSendCPU(conn WebSocketWriter) error {
cpuData, err := m.cpuCollector.Collect()
if err != nil {
return err
}
return m.sendMetrics(conn, protocol.MetricTypeCPU, cpuData)
}
// CollectAndSendMemory 采集并发送内存指标
func (m *Manager) CollectAndSendMemory(conn WebSocketWriter) error {
memData, err := m.memoryCollector.Collect()
if err != nil {
return err
}
return m.sendMetrics(conn, protocol.MetricTypeMemory, memData)
}
// CollectAndSendDisk 采集并发送磁盘指标
func (m *Manager) CollectAndSendDisk(conn WebSocketWriter) error {
diskDataList, err := m.diskCollector.Collect()
if err != nil || len(diskDataList) == 0 {
// 采集失败或无有效数据,不发送空列表
return nil
}
return m.sendMetrics(conn, protocol.MetricTypeDisk, diskDataList)
}
// CollectAndSendDiskIO 采集并发送磁盘 IO 指标
func (m *Manager) CollectAndSendDiskIO(conn WebSocketWriter) error {
diskIODataList, err := m.diskIOCollector.Collect()
if err != nil {
return err
}
return m.sendMetrics(conn, protocol.MetricTypeDiskIO, diskIODataList)
}
// CollectAndSendNetwork 采集并发送网络指标
func (m *Manager) CollectAndSendNetwork(conn WebSocketWriter) error {
networkDataList, err := m.networkCollector.Collect()
if err != nil || len(networkDataList) == 0 {
// 采集失败或无有效数据,不发送空列表
return nil
}
return m.sendMetrics(conn, protocol.MetricTypeNetwork, networkDataList)
}
// CollectAndSendNetworkConnection 采集并发送网络连接统计
func (m *Manager) CollectAndSendNetworkConnection(conn WebSocketWriter) error {
connectionData, err := m.networkConnectionCollector.Collect()
if err != nil {
return err
}
return m.sendMetrics(conn, protocol.MetricTypeNetworkConnection, connectionData)
}
// CollectAndSendHost 采集并发送主机信息
func (m *Manager) CollectAndSendHost(conn WebSocketWriter) error {
hostData, err := m.hostCollector.Collect()
if err != nil {
return err
}
return m.sendMetrics(conn, protocol.MetricTypeHost, hostData)
}
// CollectAndSendGPU 采集并发送 GPU 指标
func (m *Manager) CollectAndSendGPU(conn WebSocketWriter) error {
gpuDataList, err := m.gpuCollector.Collect()
if err != nil || len(gpuDataList) == 0 {
// GPU 监控不是必须的,失败或无数据时直接返回
return nil
}
return m.sendMetrics(conn, protocol.MetricTypeGPU, gpuDataList)
}
// CollectAndSendTemperature 采集并发送温度信息
func (m *Manager) CollectAndSendTemperature(conn WebSocketWriter) error {
tempDataList, err := m.temperatureCollector.Collect()
if err != nil || len(tempDataList) == 0 {
// 温度监控不是必须的,失败或无数据时直接返回
return nil
}
return m.sendMetrics(conn, protocol.MetricTypeTemperature, tempDataList)
}
// CollectAndSendMonitor 采集并发送监控数据
func (m *Manager) CollectAndSendMonitor(conn WebSocketWriter, items []protocol.MonitorItem) error {
monitorDataList := m.monitorCollector.Collect(items)
return m.sendMetrics(conn, protocol.MetricTypeMonitor, monitorDataList)
}
// UpdateDDNSConfig 更新 DDNS 配置
func (m *Manager) UpdateDDNSConfig(config *protocol.DDNSConfigData) {
if config == nil || !config.Enabled {
m.ddnsCollector = nil
return
}
if m.ddnsCollector == nil {
m.ddnsCollector = NewDDNSCollector(config)
} else {
m.ddnsCollector.UpdateConfig(config)
}
}
// CollectAndSendDDNSIP 采集并发送 DDNS IP 地址
func (m *Manager) CollectAndSendDDNSIP(conn WebSocketWriter) error {
if m.ddnsCollector == nil {
return nil // DDNS 未启用,静默返回
}
ipData, err := m.ddnsCollector.Collect()
if err != nil {
return err
}
// 只有当至少有一个 IP 地址时才发送
if ipData.IPv4 == "" && ipData.IPv6 == "" {
return nil
}
return conn.WriteJSON(protocol.OutboundMessage{
Type: protocol.MessageTypeDDNSIPReport,
Data: ipData,
})
}
// sendMetrics 发送指标数据
func (m *Manager) sendMetrics(conn WebSocketWriter, metricType protocol.MetricType, data interface{}) error {
return conn.WriteJSON(protocol.OutboundMessage{
Type: protocol.MessageTypeMetrics,
Data: protocol.MetricsPayload{
Type: metricType,
Data: data,
Timestamp: time.Now().UnixMilli(),
},
})
}
// GetPublicIP 通过 API 获取公网 IP 地址
func (m *Manager) GetPublicIP(apiURL string, isIPv6 bool) (string, error) {
collector := NewDDNSCollector(&protocol.DDNSConfigData{
Enabled: true,
})
return collector.GetIPFromAPI(apiURL, isIPv6)
}
// GetInterfaceIP 从网络接口获取 IP 地址
func (m *Manager) GetInterfaceIP(interfaceName string, isIPv6 bool) (string, error) {
collector := NewDDNSCollector(&protocol.DDNSConfigData{
Enabled: true,
})
return collector.GetIPFromInterface(interfaceName, isIPv6)
}