From 4df28e3536b89ae936ddf6ef54db3c7ddd8ddfdd Mon Sep 17 00:00:00 2001 From: dushixiang Date: Wed, 25 Feb 2026 14:28:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=8E=A2=E9=92=88=E3=80=81?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=9B=91=E6=8E=A7=E6=97=B6=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=88=A0=E9=99=A4=20victoriametrics=20=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/handler/agent_admin_handler.go | 9 +++++ internal/handler/monitor_handler.go | 8 +++++ internal/service/metric_service.go | 44 +++++++++++++++++++++++++ internal/vmclient/client.go | 34 +++++++++++++++++++ 4 files changed, 95 insertions(+) diff --git a/internal/handler/agent_admin_handler.go b/internal/handler/agent_admin_handler.go index add9e3f..855fa02 100644 --- a/internal/handler/agent_admin_handler.go +++ b/internal/handler/agent_admin_handler.go @@ -229,6 +229,15 @@ func (h *AgentHandler) Delete(c echo.Context) error { client.Conn.Close() } + // 删除探针在 VictoriaMetrics 中的历史指标数据 + if err := h.metricService.DeleteAgentMetrics(ctx, agentID); err != nil { + h.logger.Error("删除探针 VictoriaMetrics 数据失败", + zap.String("agentID", agentID), + zap.String("name", agent.Name), + zap.Error(err)) + return orz.NewError(500, "删除探针指标数据失败") + } + // 删除探针及其所有相关数据 if err := h.agentService.DeleteAgent(ctx, agentID); err != nil { h.logger.Error("删除探针失败", diff --git a/internal/handler/monitor_handler.go b/internal/handler/monitor_handler.go index 29ca6fb..9a53362 100644 --- a/internal/handler/monitor_handler.go +++ b/internal/handler/monitor_handler.go @@ -124,6 +124,14 @@ func (h *MonitorHandler) Delete(c echo.Context) error { id := c.Param("id") ctx := c.Request().Context() + // 删除服务监控在 VictoriaMetrics 中的历史指标数据 + if err := h.metricService.DeleteMonitorMetrics(ctx, id); err != nil { + h.logger.Error("删除服务监控 VictoriaMetrics 数据失败", + zap.String("monitorID", id), + zap.Error(err)) + return orz.NewError(500, "删除服务监控指标数据失败") + } + if err := h.monitorService.DeleteMonitor(ctx, id); err != nil { return err } diff --git a/internal/service/metric_service.go b/internal/service/metric_service.go index ca1e3e7..a403cc6 100644 --- a/internal/service/metric_service.go +++ b/internal/service/metric_service.go @@ -344,6 +344,50 @@ func (s *MetricService) GetLatestMetrics(agentID string) (*metric.LatestMetrics, return metrics, ok } +// DeleteAgentMetrics 删除探针在 VictoriaMetrics 中的历史指标 +func (s *MetricService) DeleteAgentMetrics(ctx context.Context, agentID string) error { + if agentID == "" { + return fmt.Errorf("agentID 不能为空") + } + + // 删除内存中的最新指标缓存 + s.latestCache.Delete(agentID) + + // 删除 VictoriaMetrics 中该探针的所有 pika 指标 + matcher := fmt.Sprintf(`{__name__=~"pika_.*",agent_id=%q}`, agentID) + if err := s.vmClient.DeleteSeries(ctx, []string{matcher}); err != nil { + s.logger.Error("删除探针 VictoriaMetrics 数据失败", + zap.String("agentID", agentID), + zap.Error(err)) + return err + } + + s.logger.Info("探针 VictoriaMetrics 数据删除成功", zap.String("agentID", agentID)) + return nil +} + +// DeleteMonitorMetrics 删除服务监控在 VictoriaMetrics 中的历史指标 +func (s *MetricService) DeleteMonitorMetrics(ctx context.Context, monitorID string) error { + if monitorID == "" { + return fmt.Errorf("monitorID 不能为空") + } + + // 删除内存中的监控缓存 + s.monitorLatestCache.Delete(monitorID) + + // 删除 VictoriaMetrics 中该监控任务的所有 pika monitor 指标 + matcher := fmt.Sprintf(`{__name__=~"pika_monitor_.*",monitor_id=%q}`, monitorID) + if err := s.vmClient.DeleteSeries(ctx, []string{matcher}); err != nil { + s.logger.Error("删除服务监控 VictoriaMetrics 数据失败", + zap.String("monitorID", monitorID), + zap.Error(err)) + return err + } + + s.logger.Info("服务监控 VictoriaMetrics 数据删除成功", zap.String("monitorID", monitorID)) + return nil +} + // GetAvailableNetworkInterfaces 获取探针的可用网卡列表(从 VictoriaMetrics 查询) func (s *MetricService) GetAvailableNetworkInterfaces(ctx context.Context, agentID string) ([]string, error) { // 查询 interface label 的所有值,排除空字符串(汇总数据) diff --git a/internal/vmclient/client.go b/internal/vmclient/client.go index 4ddae3b..896ab56 100644 --- a/internal/vmclient/client.go +++ b/internal/vmclient/client.go @@ -220,6 +220,40 @@ func (c *VMClient) Query(ctx context.Context, query string) (*QueryResult, error return &result, nil } +// DeleteSeries 删除时间序列数据 +func (c *VMClient) DeleteSeries(ctx context.Context, matchers []string) error { + if len(matchers) == 0 { + return nil + } + + reqCtx, cancel := context.WithTimeout(ctx, c.queryTimeout) + defer cancel() + + params := url.Values{} + for _, matcher := range matchers { + params.Add("match[]", matcher) + } + + reqURL := fmt.Sprintf("%s/api/v1/admin/tsdb/delete_series?%s", c.baseURL, params.Encode()) + req, err := http.NewRequestWithContext(reqCtx, http.MethodPost, reqURL, nil) + if err != nil { + return fmt.Errorf("create request failed: %w", err) + } + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("delete series failed: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("delete series failed with status %d: %s", resp.StatusCode, string(body)) + } + + return nil +} + // ConvertToDataPoints 将查询结果转换为数据点列表 func ConvertToDataPoints(result *QueryResult) []DataPoint { if result == nil || len(result.Data.Result) == 0 {