Files
mfd-monitor-agent/internal/reporter/reporter.go
codex 877e4cf41d docs(agent): 统一中文注释并补充部署文档
将代码与测试注释改为中文以符合仓库规范;新增中文部署指南、
示例配置,并完善构建脚本对 VERSION 注入的说明。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 02:30:49 +00:00

100 lines
2.5 KiB
Go

package reporter
import (
"bytes"
"context"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"time"
"mfd-monitor-agent/internal/status"
)
const defaultRetryDelay = 2 * time.Second
// Provider 提供用于外发上报的缓存 HostStatus。
type Provider interface {
// Get 返回最新快照及快照是否已就绪。
Get() (status.HostStatus, bool)
}
// Client 将 HostStatus 快照 POST 到配置的中心端地址。
type Client struct {
endpoint string
client *http.Client
}
// NewClient 使用给定端点与请求超时创建上报客户端。
func NewClient(endpoint string, timeout time.Duration) *Client {
return &Client{
endpoint: endpoint,
client: &http.Client{Timeout: timeout},
}
}
// Report 将一份 HostStatus 以 JSON POST 上报;非 2xx 响应作为错误返回。
func (c *Client) Report(snapshot status.HostStatus) error {
body, err := json.Marshal(snapshot)
if err != nil {
return err
}
req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := c.client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return fmt.Errorf("report returned HTTP %d", resp.StatusCode)
}
return nil
}
// Run 在 ctx 取消前按间隔 POST 最新缓存快照。
func Run(ctx context.Context, provider Provider, client *Client, interval time.Duration, logger *slog.Logger) {
runWithRetryDelay(ctx, provider, client, interval, defaultRetryDelay, logger)
}
// runWithRetryDelay 运行上报循环;单次失败后按 retryDelay 重试一次。
func runWithRetryDelay(ctx context.Context, provider Provider, client *Client, interval time.Duration, retryDelay time.Duration, logger *slog.Logger) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
snapshot, ok := provider.Get()
if !ok {
continue
}
if err := reportWithRetry(ctx, client, snapshot, retryDelay); err != nil {
logger.Warn("status report failed", "error", err)
}
}
}
}
// reportWithRetry 上报快照;失败时在可取消的等待后重试一次。
func reportWithRetry(ctx context.Context, client *Client, snapshot status.HostStatus, retryDelay time.Duration) error {
err := client.Report(snapshot)
if err == nil {
return nil
}
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(retryDelay):
}
if retryErr := client.Report(snapshot); retryErr != nil {
return retryErr
}
return nil
}