重构数据采集打包,修改为统一打包发送
This commit is contained in:
1 parent
29d8b3e9cf
commit
e115d828ab
5 files changed
+169
-283
No files matched your search
@@ -80,9 +80,6 @@ func (h *AgentHandler) handleWebSocketMessage(ctx context.Context, agentID strin
|
||||
case protocol.MessageTypeMetrics:
|
||||
return h.handleMetricsMessage(ctx, agentID, data)
|
||||
|
||||
case protocol.MessageTypeBundle:
|
||||
return h.handleBundleMessage(ctx, agentID, data)
|
||||
|
||||
case protocol.MessageTypeCommandResp:
|
||||
return h.handleCommandResponseMessage(ctx, agentID, data)
|
||||
|
||||
@@ -164,31 +161,19 @@ func (h *AgentHandler) handleWebSocketPong(agentID string) {
|
||||
}
|
||||
|
||||
func (h *AgentHandler) handleMetricsMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
||||
var metricsWrapper protocol.MetricsPayload
|
||||
if err := json.Unmarshal(data, &metricsWrapper); err != nil {
|
||||
return err
|
||||
}
|
||||
metricsData, err := json.Marshal(metricsWrapper.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.metricService.HandleMetricData(ctx, agentID, string(metricsWrapper.Type), metricsData, metricsWrapper.Timestamp)
|
||||
}
|
||||
|
||||
func (h *AgentHandler) handleBundleMessage(ctx context.Context, agentID string, data json.RawMessage) error {
|
||||
var bundle protocol.BundlePayload
|
||||
if err := json.Unmarshal(data, &bundle); err != nil {
|
||||
var batch protocol.MetricsBatch
|
||||
if err := json.Unmarshal(data, &batch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, item := range bundle.Items {
|
||||
metricsData, err := json.Marshal(item.Data)
|
||||
for _, sample := range batch.Samples {
|
||||
metricsData, err := json.Marshal(sample.Data)
|
||||
if err != nil {
|
||||
h.logger.Warn("failed to marshal bundled metric item", zap.Error(err))
|
||||
h.logger.Warn("failed to marshal metric sample", zap.Error(err))
|
||||
continue
|
||||
}
|
||||
if err := h.metricService.HandleMetricData(ctx, agentID, string(item.Type), metricsData, item.Timestamp); err != nil {
|
||||
h.logger.Warn("failed to handle bundled metric item", zap.Error(err), zap.String("type", string(item.Type)))
|
||||
if err := h.metricService.HandleMetricData(ctx, agentID, string(sample.Type), metricsData, sample.Timestamp); err != nil {
|
||||
h.logger.Warn("failed to handle metric sample", zap.Error(err), zap.String("type", string(sample.Type)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,17 +37,18 @@ type AgentInfo struct {
|
||||
Version string `json:"version"` // 版本号
|
||||
}
|
||||
|
||||
// MetricsPayload 指标数据包装,发送端/接收端统一使用
|
||||
type MetricsPayload struct {
|
||||
// MetricSample 单条指标采样
|
||||
type MetricSample struct {
|
||||
Type MetricType `json:"type"`
|
||||
Data interface{} `json:"data"`
|
||||
Timestamp int64 `json:"timestamp,omitempty"` // 客户端采集时间(毫秒)
|
||||
}
|
||||
|
||||
// BundlePayload 批量指标数据包装
|
||||
type BundlePayload struct {
|
||||
Items []MetricsPayload `json:"items"`
|
||||
// MetricsBatch 一次采集周期内的批量指标,是 metrics 上报的唯一载体
|
||||
type MetricsBatch struct {
|
||||
Samples []MetricSample `json:"samples"`
|
||||
}
|
||||
|
||||
type MessageType string
|
||||
|
||||
// 控制消息
|
||||
@@ -58,9 +59,8 @@ const (
|
||||
MessageTypeCommand MessageType = "command"
|
||||
MessageTypeCommandResp MessageType = "command_response"
|
||||
MessageTypeUninstall MessageType = "uninstall"
|
||||
// 指标消息
|
||||
// 指标消息(批量)
|
||||
MessageTypeMetrics MessageType = "metrics"
|
||||
MessageTypeBundle MessageType = "bundle"
|
||||
MessageTypeMonitorConfig MessageType = "monitor_config"
|
||||
// 防篡改消息
|
||||
MessageTypeTamperProtect MessageType = "tamper_protect"
|
||||
|
||||
+87
-125
@@ -1,18 +1,18 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/dushixiang/pika/internal/protocol"
|
||||
"github.com/dushixiang/pika/pkg/agent/config"
|
||||
)
|
||||
|
||||
// WebSocketWriter 定义 WebSocket 写入接口
|
||||
type WebSocketWriter interface {
|
||||
WriteJSON(v interface{}) error
|
||||
}
|
||||
// ErrNoData 表示采集成功但当前周期无有效数据可上报(如 GPU 不存在、磁盘列表为空)。
|
||||
// 调用方应静默跳过,不视为错误。
|
||||
var ErrNoData = errors.New("no data")
|
||||
|
||||
// Manager 采集器管理器
|
||||
// Manager 采集器管理器:纯采集,不做 I/O
|
||||
type Manager struct {
|
||||
cpuCollector *CPUCollector
|
||||
memoryCollector *MemoryCollector
|
||||
@@ -24,7 +24,6 @@ type Manager struct {
|
||||
temperatureCollector *TemperatureCollector
|
||||
gpuCollector *GPUCollector
|
||||
monitorCollector *MonitorCollector
|
||||
ddnsCollector *DDNSCollector
|
||||
}
|
||||
|
||||
// NewManager 创建采集器管理器
|
||||
@@ -40,152 +39,115 @@ func NewManager(cfg *config.Config) *Manager {
|
||||
temperatureCollector: NewTemperatureCollector(),
|
||||
gpuCollector: NewGPUCollector(),
|
||||
monitorCollector: NewMonitorCollector(),
|
||||
ddnsCollector: nil, // DDNS 采集器需要配置后才能初始化
|
||||
}
|
||||
}
|
||||
|
||||
// CollectAndSendCPU 采集并发送 CPU 指标
|
||||
func (m *Manager) CollectAndSendCPU(conn WebSocketWriter) error {
|
||||
cpuData, err := m.cpuCollector.Collect()
|
||||
// makeSample 构造一条 MetricSample
|
||||
func makeSample(t protocol.MetricType, data interface{}) protocol.MetricSample {
|
||||
return protocol.MetricSample{
|
||||
Type: t,
|
||||
Data: data,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
}
|
||||
}
|
||||
|
||||
// CollectCPU 采集 CPU 指标
|
||||
func (m *Manager) CollectCPU() (protocol.MetricSample, error) {
|
||||
data, err := m.cpuCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
|
||||
return m.sendMetrics(conn, protocol.MetricTypeCPU, cpuData)
|
||||
return makeSample(protocol.MetricTypeCPU, data), nil
|
||||
}
|
||||
|
||||
// CollectAndSendMemory 采集并发送内存指标
|
||||
func (m *Manager) CollectAndSendMemory(conn WebSocketWriter) error {
|
||||
memData, err := m.memoryCollector.Collect()
|
||||
// CollectMemory 采集内存指标
|
||||
func (m *Manager) CollectMemory() (protocol.MetricSample, error) {
|
||||
data, err := m.memoryCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
|
||||
return m.sendMetrics(conn, protocol.MetricTypeMemory, memData)
|
||||
return makeSample(protocol.MetricTypeMemory, data), nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
// CollectDisk 采集磁盘指标。无有效数据时返回 ErrNoData。
|
||||
func (m *Manager) CollectDisk() (protocol.MetricSample, error) {
|
||||
data, err := m.diskCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
return m.sendMetrics(conn, protocol.MetricTypeDiskIO, diskIODataList)
|
||||
if len(data) == 0 {
|
||||
return protocol.MetricSample{}, ErrNoData
|
||||
}
|
||||
return makeSample(protocol.MetricTypeDisk, data), nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
// CollectDiskIO 采集磁盘 IO 指标
|
||||
func (m *Manager) CollectDiskIO() (protocol.MetricSample, error) {
|
||||
data, err := m.diskIOCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
return m.sendMetrics(conn, protocol.MetricTypeNetworkConnection, connectionData)
|
||||
return makeSample(protocol.MetricTypeDiskIO, data), nil
|
||||
}
|
||||
|
||||
// CollectAndSendHost 采集并发送主机信息
|
||||
func (m *Manager) CollectAndSendHost(conn WebSocketWriter) error {
|
||||
hostData, err := m.hostCollector.Collect()
|
||||
// CollectNetwork 采集网络指标。无有效数据时返回 ErrNoData。
|
||||
func (m *Manager) CollectNetwork() (protocol.MetricSample, error) {
|
||||
data, err := m.networkCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
|
||||
return m.sendMetrics(conn, protocol.MetricTypeHost, hostData)
|
||||
if len(data) == 0 {
|
||||
return protocol.MetricSample{}, ErrNoData
|
||||
}
|
||||
return makeSample(protocol.MetricTypeNetwork, data), nil
|
||||
}
|
||||
|
||||
// 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()
|
||||
// CollectNetworkConnection 采集网络连接统计
|
||||
func (m *Manager) CollectNetworkConnection() (protocol.MetricSample, error) {
|
||||
data, err := m.networkConnectionCollector.Collect()
|
||||
if err != nil {
|
||||
return err
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
|
||||
// 只有当至少有一个 IP 地址时才发送
|
||||
if ipData.IPv4 == "" && ipData.IPv6 == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return conn.WriteJSON(protocol.OutboundMessage{
|
||||
Type: protocol.MessageTypeDDNSIPReport,
|
||||
Data: ipData,
|
||||
})
|
||||
return makeSample(protocol.MetricTypeNetworkConnection, data), nil
|
||||
}
|
||||
|
||||
// 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(),
|
||||
},
|
||||
})
|
||||
// CollectHost 采集主机信息(含 Load)
|
||||
func (m *Manager) CollectHost() (protocol.MetricSample, error) {
|
||||
data, err := m.hostCollector.Collect()
|
||||
if err != nil {
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
return makeSample(protocol.MetricTypeHost, data), nil
|
||||
}
|
||||
|
||||
// CollectGPU 采集 GPU 指标。无 GPU 时返回 ErrNoData(GPU 不是必备)
|
||||
func (m *Manager) CollectGPU() (protocol.MetricSample, error) {
|
||||
data, err := m.gpuCollector.Collect()
|
||||
if err != nil {
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return protocol.MetricSample{}, ErrNoData
|
||||
}
|
||||
return makeSample(protocol.MetricTypeGPU, data), nil
|
||||
}
|
||||
|
||||
// CollectTemperature 采集温度信息。无传感器时返回 ErrNoData(温度不是必备)
|
||||
func (m *Manager) CollectTemperature() (protocol.MetricSample, error) {
|
||||
data, err := m.temperatureCollector.Collect()
|
||||
if err != nil {
|
||||
return protocol.MetricSample{}, err
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return protocol.MetricSample{}, ErrNoData
|
||||
}
|
||||
return makeSample(protocol.MetricTypeTemperature, data), nil
|
||||
}
|
||||
|
||||
// CollectMonitor 采集服务监控数据(HTTP/TCP 探活)
|
||||
func (m *Manager) CollectMonitor(items []protocol.MonitorItem) protocol.MetricSample {
|
||||
data := m.monitorCollector.Collect(items)
|
||||
return makeSample(protocol.MetricTypeMonitor, data)
|
||||
}
|
||||
|
||||
// GetPublicIP 通过 API 获取公网 IP 地址
|
||||
|
||||
+62
-127
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
@@ -28,10 +29,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
agentPingInterval = 10 * time.Second
|
||||
agentPongWait = 30 * time.Second
|
||||
agentWriteWait = 5 * time.Second
|
||||
agentCollectTimeout = 30 * time.Second
|
||||
agentPingInterval = 10 * time.Second
|
||||
agentPongWait = 30 * time.Second
|
||||
agentWriteWait = 5 * time.Second
|
||||
agentCollectTimeout = 30 * time.Second
|
||||
)
|
||||
|
||||
// safeConn 线程安全的 WebSocket 连接包装器
|
||||
@@ -162,8 +163,8 @@ func (a *Agent) runOnce(ctx context.Context, onRegistered func()) error {
|
||||
|
||||
// 创建自定义的 Dialer
|
||||
dialer := &websocket.Dialer{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
EnableCompression: true,
|
||||
}
|
||||
if a.cfg.Server.InsecureSkipVerify {
|
||||
@@ -429,10 +430,14 @@ func (a *Agent) handleMonitorConfig(data json.RawMessage) {
|
||||
|
||||
slog.Info("收到服务监控配置,立即执行检测", "count", len(payload.Items))
|
||||
|
||||
// 立即执行一次监控检测
|
||||
// 立即执行一次监控检测,结果以单元素 batch 上报
|
||||
sample := manager.CollectMonitor(payload.Items)
|
||||
writer := newOutboundWriter(a.getActiveConn(), a.outboundBuffer)
|
||||
if err := manager.CollectAndSendMonitor(writer, payload.Items); err != nil {
|
||||
slog.Warn("监控检测失败", "error", err)
|
||||
if err := writer.WriteJSON(protocol.OutboundMessage{
|
||||
Type: protocol.MessageTypeMetrics,
|
||||
Data: protocol.MetricsBatch{Samples: []protocol.MetricSample{sample}},
|
||||
}); err != nil {
|
||||
slog.Warn("发送监控结果失败", "error", err)
|
||||
} else {
|
||||
slog.Info("服务监控检测完成,已上报或缓存监控项结果", "count", len(payload.Items))
|
||||
}
|
||||
@@ -511,26 +516,6 @@ func (a *Agent) metricsLoop(ctx context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
type bundleWriter struct {
|
||||
items []protocol.MetricsPayload
|
||||
}
|
||||
|
||||
func (w *bundleWriter) WriteJSON(v interface{}) error {
|
||||
msg, ok := v.(protocol.OutboundMessage)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid message type for bundleWriter")
|
||||
}
|
||||
|
||||
if msg.Type == protocol.MessageTypeMetrics {
|
||||
payload, ok := msg.Data.(protocol.MetricsPayload)
|
||||
if ok {
|
||||
w.items = append(w.items, payload)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("only metrics messages can be bundled")
|
||||
}
|
||||
|
||||
// collectAndSendAllMetrics 采集并发送所有动态指标
|
||||
func (a *Agent) collectAndSendAllMetrics(manager *collector.Manager) error {
|
||||
if manager == nil {
|
||||
@@ -566,12 +551,15 @@ func newCollectTimer(name string) *collectTimer {
|
||||
|
||||
func (t *collectTimer) done() {
|
||||
d := time.Since(t.start)
|
||||
if d > 500*time.Millisecond {
|
||||
if d > 1500*time.Millisecond {
|
||||
slog.Info("采集耗时", "collector", t.name, "duration", d)
|
||||
}
|
||||
}
|
||||
|
||||
// doCollectAndSend 执行实际的采集和发送
|
||||
// collectFn 单个采集器的采集函数签名
|
||||
type collectFn func() (protocol.MetricSample, error)
|
||||
|
||||
// doCollectAndSend 采集所有动态指标,打包成一个 batch 上报
|
||||
func (a *Agent) doCollectAndSend(manager *collector.Manager) error {
|
||||
conn := a.getActiveConn()
|
||||
if conn != nil {
|
||||
@@ -582,108 +570,55 @@ func (a *Agent) doCollectAndSend(manager *collector.Manager) error {
|
||||
}
|
||||
}
|
||||
|
||||
writer := newOutboundWriter(conn, a.outboundBuffer)
|
||||
bw := &bundleWriter{}
|
||||
// required=true 时采集失败计入错误;required=false 适用于 GPU/温度等可选项
|
||||
collectors := []struct {
|
||||
name string
|
||||
required bool
|
||||
fn collectFn
|
||||
}{
|
||||
{"cpu", true, manager.CollectCPU},
|
||||
{"memory", true, manager.CollectMemory},
|
||||
{"disk", true, manager.CollectDisk},
|
||||
{"disk_io", true, manager.CollectDiskIO},
|
||||
{"network", true, manager.CollectNetwork},
|
||||
{"network_connection", true, manager.CollectNetworkConnection},
|
||||
{"host", true, manager.CollectHost},
|
||||
{"gpu", false, manager.CollectGPU},
|
||||
{"temperature", false, manager.CollectTemperature},
|
||||
}
|
||||
|
||||
samples := make([]protocol.MetricSample, 0, len(collectors))
|
||||
var hasError bool
|
||||
|
||||
// CPU 动态指标
|
||||
func() {
|
||||
t := newCollectTimer("cpu")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendCPU(bw); err != nil {
|
||||
slog.Warn("采集CPU指标失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
for _, c := range collectors {
|
||||
func() {
|
||||
t := newCollectTimer(c.name)
|
||||
defer t.done()
|
||||
sample, err := c.fn()
|
||||
if err != nil {
|
||||
if errors.Is(err, collector.ErrNoData) {
|
||||
return
|
||||
}
|
||||
if c.required {
|
||||
slog.Warn("采集指标失败", "collector", c.name, "error", err)
|
||||
hasError = true
|
||||
} else {
|
||||
slog.Info("采集可选指标失败", "collector", c.name, "error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
samples = append(samples, sample)
|
||||
}()
|
||||
}
|
||||
|
||||
// 内存动态指标
|
||||
func() {
|
||||
t := newCollectTimer("memory")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendMemory(bw); err != nil {
|
||||
slog.Warn("采集内存指标失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
writer := newOutboundWriter(conn, a.outboundBuffer)
|
||||
|
||||
// 磁盘指标
|
||||
func() {
|
||||
t := newCollectTimer("disk")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendDisk(bw); err != nil {
|
||||
slog.Warn("采集磁盘指标失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
|
||||
// 磁盘 IO 指标
|
||||
func() {
|
||||
t := newCollectTimer("disk_io")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendDiskIO(bw); err != nil {
|
||||
slog.Warn("采集磁盘IO指标失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
|
||||
// 网络指标
|
||||
func() {
|
||||
t := newCollectTimer("network")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendNetwork(bw); err != nil {
|
||||
slog.Warn("采集网络指标失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
|
||||
// 网络连接统计
|
||||
func() {
|
||||
t := newCollectTimer("network_connection")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendNetworkConnection(bw); err != nil {
|
||||
slog.Warn("采集网络连接统计失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
|
||||
// 主机信息(包含 Load)
|
||||
func() {
|
||||
t := newCollectTimer("host")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendHost(bw); err != nil {
|
||||
slog.Warn("采集主机信息失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}()
|
||||
|
||||
// GPU 信息(可选)
|
||||
func() {
|
||||
t := newCollectTimer("gpu")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendGPU(bw); err != nil {
|
||||
slog.Info("采集GPU信息失败", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 温度信息(可选)
|
||||
func() {
|
||||
t := newCollectTimer("temperature")
|
||||
defer t.done()
|
||||
if err := manager.CollectAndSendTemperature(bw); err != nil {
|
||||
slog.Info("采集温度信息失败", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 发送打包后的指标
|
||||
if len(bw.items) > 0 {
|
||||
bundle := protocol.BundlePayload{
|
||||
Items: bw.items,
|
||||
}
|
||||
if len(samples) > 0 {
|
||||
if err := writer.WriteJSON(protocol.OutboundMessage{
|
||||
Type: protocol.MessageTypeBundle,
|
||||
Data: bundle,
|
||||
Type: protocol.MessageTypeMetrics,
|
||||
Data: protocol.MetricsBatch{Samples: samples},
|
||||
}); err != nil {
|
||||
slog.Warn("发送打包指标失败", "error", err)
|
||||
slog.Warn("发送指标 batch 失败", "error", err)
|
||||
hasError = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/dushixiang/pika/internal/protocol"
|
||||
"github.com/dushixiang/pika/pkg/agent/collector"
|
||||
"github.com/dushixiang/pika/pkg/agent/utils"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
@@ -23,6 +22,11 @@ const (
|
||||
outboundBufferRetention = 24 * time.Hour
|
||||
)
|
||||
|
||||
// WebSocketWriter 抽象 JSON 写入操作,由 outboundBuffer 和 outboundWriter 共用
|
||||
type WebSocketWriter interface {
|
||||
WriteJSON(v interface{}) error
|
||||
}
|
||||
|
||||
type outboundBuffer struct {
|
||||
path string
|
||||
mu sync.Mutex
|
||||
@@ -101,7 +105,7 @@ func (b *outboundBuffer) Append(v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *outboundBuffer) Flush(writer collector.WebSocketWriter) (int, error) {
|
||||
func (b *outboundBuffer) Flush(writer WebSocketWriter) (int, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
|
||||
Reference in new issue
Block a user