Files
mfd-monitor-agent/internal/snapshot/snapshot.go
codex de3fd88948 ✨ feat(agent): 添加 token 鉴权和流程文档
* 默认监听调整为 0.0.0.0:8080,并要求查询参数 token
* 支持首次 run 自动生成含随机 token 的配置文件
* 补充 HTTP 鉴权、配置校验和相关测试
* 完善代码注释,覆盖缓存、上报、服务和平台采集路径
* 新增架构与开发流程文档,补充一键构建脚本
2026-05-22 06:46:21 +00:00

114 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package snapshot
import (
"context"
"sync"
"time"
"mfd-monitor-agent/internal/status"
)
// Collector 将 HostStatus 的一个子区块采集并写入提供的快照。
type Collector interface {
// Name 返回用于 partial 快照错误记录的稳定采集器名称。
Name() string
// Collect 填充 out 中对应子区块;该子区块不完整时返回错误。
Collect(context.Context, *status.HostStatus) error
}
// CollectorFunc 将函数适配为 Collector,供测试与小型采集器使用。
type CollectorFunc func(context.Context, *status.HostStatus) error
// Name 为基于函数的采集器返回通用名称。
func (fn CollectorFunc) Name() string {
return "collector"
}
// Collect 调用包装的采集函数。
func (fn CollectorFunc) Collect(ctx context.Context, out *status.HostStatus) error {
return fn(ctx, out)
}
// Cache 在并发安全接口后保存最近一次 HostStatus 快照。
type Cache struct {
// collectors 是按固定顺序执行的子采集器集合。
collectors []Collector
// version 和 startedAt 会写入每次快照的 agent 元数据。
version string
startedAt time.Time
// mu 保护 last 和 hasLast,允许 HTTP 与上报并发读取缓存。
mu sync.RWMutex
// last 保存最近一次完整或 partial 的 HostStatus。
last status.HostStatus
// hasLast 表示首次采集是否已经完成。
hasLast bool
}
// NewCache 使用给定采集器与 agent 元数据创建空的快照缓存。
func NewCache(collectors []Collector, version string, startedAt time.Time) *Cache {
return &Cache{collectors: collectors, version: version, startedAt: startedAt}
}
// Collect 运行全部采集器一次并存储结果;部分采集器失败时仍保存快照。
func (c *Cache) Collect(ctx context.Context) status.HostStatus {
out := status.HostStatus{
Agent: status.AgentInfo{
Version: c.version,
StartedAt: c.startedAt,
SnapshotAt: time.Now().UTC(),
SnapshotStatus: "ok",
},
Processes: status.ProcessResult{Status: "ok"},
TCPListeners: status.TCPResult{Status: "ok"},
}
for _, collector := range c.collectors {
if err := collector.Collect(ctx, &out); err != nil {
// 单个子采集器失败只让整份快照降级为 partial,其他已成功字段继续对外可用。
out.Agent.SnapshotStatus = "partial"
out.Errors = append(out.Errors, status.StatusError{
Collector: collector.Name(),
Message: err.Error(),
})
}
}
// 发布快照时一次性替换缓存,避免读者看到半写入的结构。
c.mu.Lock()
c.last = out
c.hasLast = true
c.mu.Unlock()
return out
}
// Get 返回最新缓存快照,以及是否至少完成过一次采集。
func (c *Cache) Get() (status.HostStatus, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
return c.last, c.hasLast
}
// Run 立即采集一次,随后在 ctx 取消前按间隔刷新缓存。
func (c *Cache) Run(ctx context.Context, interval time.Duration, timeout time.Duration) {
// 启动后先同步完成一轮采集,让 /health 和 /status 尽快进入就绪状态。
c.collectWithTimeout(ctx, timeout)
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
c.collectWithTimeout(ctx, timeout)
}
}
}
// collectWithTimeout 在整体截止时间下执行一轮采集。
func (c *Cache) collectWithTimeout(ctx context.Context, timeout time.Duration) {
// 超时通过 context 传递给子采集器;无法被 context 打断的系统调用仍依赖平台返回。
collectCtx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
c.Collect(collectCtx)
}