2026/4/18 9:37:03
网站建设
项目流程
做一个购物网站,平乡县网站建设平台,做彩票网站是违法,页面模板功能一、一个让人困惑的监控图16:05 发生了什么#xff1f;下午 4 点#xff0c;订单团队的小王收到告警#xff1a;订单创建失败。他立刻打开监控平台#xff0c;点击订单监控菜单#xff0c;看到这样一张图#xff1a;alarm_log_error_total (订单创建失败) ━…一、一个让人困惑的监控图16:05 发生了什么下午 4 点订单团队的小王收到告警订单创建失败。他立刻打开监控平台点击订单监控菜单看到这样一张图alarm_log_error_total (订单创建失败) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1 | ╭─╮ | │ │ | │ │ 0 |────────────────────╯ ╰───────── └────────────────────────────────── 16:00 16:05 16:10 16:15 16:20小王懵了16:05 明明发生了错误为什么图上显示的是 0查看 Prometheus 原始数据他去 Prometheus 查询alarm_log_error_total看到时间 原始值 16:00 (不存在) 16:01 (不存在) 16:05 1 ← 第一次出现错误已发生 16:06 1 ← 保持不变 16:07 1 16:10 2 ← 又增加了1次 16:11 2原始数据清楚地记录了16:05 发生了第一次错误16:10 发生了第二次错误。可为什么图上看不出来二、问题根源Prometheus 的 increase() 不适合这个场景传统做法使用 increase()在 Grafana 或其他可视化工具中监控 Counter 指标的标准做法是increase(alarm_log_error_total[1m])这个查询的结果是时间 原始值 increase(1m) 16:05 1 0 ← 因为没有前值无法计算增量 16:06 1 0 ← 从 1 到 1增量为 0 16:07 1 0 16:08 1 0 16:09 1 0 16:10 2 1 ← 从 1 到 2增量为 1 16:11 2 0结果除了 16:10 这个点其他全是 0。小王看到的图就是基于这个结果画的——一条几乎全是 0 的线只在 16:10 有一个小凸起。为什么 increase() 会这样Prometheus 的increase()函数计算的是时间窗口内的变化量如果 Counter 从 0 变成 1increase() 1 ✓如果 Counter 保持为 1increase() 0 ✗但在告警场景中16:05 指标第一次被采集时值就已经是 1错误已经发生了后续几分钟没有新错误值保持为 1increase()认为没有变化所以返回 0Prometheus 的设计理念关注的是速率和累计趋势不是瞬时事件。但业务团队要的是我就想知道每个时刻发生了几次错误。三、我们想要什么样的图理想效果小王期望看到的图应该是这样alarm_log_error_total (订单创建失败次数) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 1 | ╭─╮ ╭─╮ | │ │ │ │ | │ │ │ │ 0 |─────╯ ╰─────────╯ ╰────────── └────────────────────────────────── 16:00 16:05 16:10 16:15 16:20 ↑ ↑ 第一次错误 第二次错误清晰地看到16:05 有一个波峰值为 116:10 有一个波峰值为 1其他时刻值为 0没有新增错误对比两种图时刻Prometheus 原始值传统 increase()我们想要的16:0510 ✗1 ✓16:0610 ✓0 ✓16:1021 ✓1 ✓16:1120 ✓0 ✓关键差异16:05 这个时刻increase()返回 0但我们想要的是 1。四、解决方案计算相邻点的差值核心思路不使用 Prometheus 的increase()而是查询原始 Counter 值在后端计算相邻两个点的差值把差值返回给前端原始值: [0, 0, 0, 1, 1, 1, 2, 2, 2] 时间: [t1,t2,t3,t4,t5,t6,t7,t8,t9] 计算差值: t2: 0 - 0 0 t3: 0 - 0 0 t4: 1 - 0 1 ← 第一次错误 t5: 1 - 1 0 t6: 1 - 1 0 t7: 2 - 1 1 ← 第二次错误 t8: 2 - 2 0 t9: 2 - 2 0 返回前端: [0, 0, 1, 0, 0, 1, 0, 0] 时间: [t2,t3,t4,t5,t6,t7,t8,t9] ↑ 注意第一个点 t1 被丢弃为什么丢弃第一个点因为第一个点没有前值可比较无法计算差值。丢弃它之后每个返回的点都表示相对于前一时刻的变化。技术架构QueryRange1.补齐时间点2.计算差值3.移除首点JSONPrometheus存储原始Counter后端服务完整时序相邻点相减返回增量值前端ECharts波峰图五、后端核心实现3 步搞定步骤 1查询原始 Counter 值// 直接查询指标不使用 increase() 或 rate() query : alarm_log_error_total result, _, err : promAPI.QueryRange(ctx, query, v1.Range{ Start: startTime, End: endTime, Step: 30 * time.Second, // 30秒采样一次 }) // 返回: [[timestamp, value], ...] // 例如: [[1736049600, 0], [1736049630, 0], [1736049660, 1], ...]步骤 2补齐缺失的时间点Prometheus 不保证每个时间点都有数据指标可能还未创建需要补 0func fillMissingPoints(points [][]float64, start, end time.Time, step time.Duration) [][]float64 { pointMap : make(map[int64]float64) for _, p : range points { pointMap[int64(p[0])] p[1] } var result [][]float64 for t : start.Unix(); t end.Unix(); t int64(step.Seconds()) { if val, exists : pointMap[t]; exists { result append(result, []float64{float64(t), val}) } else { result append(result, []float64{float64(t), 0}) // 补0 } } return result }步骤 3计算相邻差值Counter 专用func processCounter(points [][]float64) [][]float64 { if len(points) 1 { return [][]float64{} } result : make([][]float64, 0, len(points)-1) for i : 1; i len(points); i { prevValue : points[i-1][1] currValue : points[i][1] timestamp : points[i][0] diff : currValue - prevValue // 处理 Counter 重置服务重启 if diff 0 { diff currValue } result append(result, []float64{timestamp, diff}) } return result // 注意长度比原始数据少1 }完整数据流Prometheus后端前端补齐缺失时间点计算相邻差值移除第一个点渲染图表查询订单监控时间范围1小时QueryRange(alarm_log_error_total)[0,0,0,1,1,1,2,2,2][[t2,0], [t3,0], [t4,1], [t5,0], [t6,0], [t7,1], [t8,0], [t9,0]]16:05和16:10出现波峰Prometheus后端前端六、Gauge 指标怎么办场景对比假设我们还有一个 SQL 查询规则统计待处理订单数SELECT COUNT(*) FROM orders WHERE status pending这个指标是Gauge 类型可增可减的瞬时值时间 查询结果 16:00 100 16:05 150 16:10 120 16:15 180业务团队想看的是当前有多少待处理订单而不是相对于上一时刻增加了多少。Gauge 不需要计算差值func processGauge(points [][]float64) [][]float64 { // 直接返回原始值不做任何处理 return points }返回前端[[16:00, 100], [16:05, 150], [16:10, 120], [16:15, 180]]前端渲染出一条平滑的曲线清晰展示数值变化趋势。Counter vs Gauge 对比维度CounterGauge语义累计值只增不减当前值可增可减业务关心增量这一分钟新增了几次绝对值现在是多少处理方式计算相邻差值保持原值示例错误次数、请求总数连接数、队列长度、SQL查询结果七、前端零负担渲染前端只做 3 件事// 1. 接收后端数据 const response await getMetricData(groupId, timeRange) // 2. 转换时间格式秒 → 毫秒 const chartData response.series.map(s ({ name: formatLegend(s.metric), type: line, data: s.points.map(p [p[0] * 1000, p[1]]) // 秒转毫秒 })) // 3. 渲染图表 chart.setOption({ xAxis: { type: time }, yAxis: { type: value }, series: chartData })不做任何差值计算、不做任何业务逻辑——所有复杂处理都在后端完成。后端返回的数据格式{ name: alarm_log_error_total, type: counter, series: [{ metric: { service: order-service, level: error }, points: [ [1736049660, 0], [1736049720, 0], [1736049780, 1], // 16:05 波峰 [1736049840, 0], [1736049900, 0], [1736049960, 1] // 16:10 波峰 ] }] }八、效果对比改造前使用 increase()16:05 发生错误 → 图上显示 0 16:10 又发生错误 → 图上显示 1业务团队看不懂改造后计算相邻差值16:05 发生错误 → 图上显示 1波峰 16:10 又发生错误 → 图上显示 1波峰业务团队一目了然九、关键设计点1. 采样粒度自适应根据查询时间长度自动调整采样间隔func calculateStep(timeRange string) time.Duration { switch timeRange { case 15m: return 15 * time.Second // 15分钟 / 60点 case 1h: return 30 * time.Second // 1小时 / 120点 case 3h: return 60 * time.Second // 3小时 / 180点 case 6h: return 2 * time.Minute // 6小时 / 180点 case 12h: return 4 * time.Minute // 12小时 / 180点 case 24h: return 8 * time.Minute // 24小时 / 180点 } }设计原则每个图表 60~180 个数据点平衡精度与性能。2. 指标类型识别从规则类型推断指标类型func getMetricType(rule Rule) string { if rule.Type keyword { return counter // 日志关键词规则 → Counter } if rule.Type query { return gauge // SQL 查询规则 → Gauge } }3. 前后端职责分离层次职责为什么后端指标类型识别、差值计算、数据补齐理解业务语义处理复杂逻辑前端时间格式转换、图表渲染专注展示保持简洁十、处理边界情况Counter 重置服务重启时Counter 从大值重置为 0时间 原始值 天真计算 修正后 16:00 100 - - 16:01 150 50 ✓ 50 ✓ 16:02 0 -150 ✗ 0 ✓ 16:03 5 5 ✓ 5 ✓修正逻辑diff : currValue - prevValue if diff 0 { diff currValue // 负数说明重置用当前值 }指标首次出现指标第一次被采集时前面的时间点都补 0时间 原始值 补齐后 差值 15:55 (无) 0 - 15:56 (无) 0 0 15:57 (无) 0 0 15:58 1 1 1 ← 第一次出现的增量十一、与 Grafana 对比维度Grafana increase()本方案Counter 首次出现显示 0看不出来显示 1清晰波峰学习成本需要懂 PromQL点击菜单即可Dashboard 配置手动配置 Panel自动生成适用场景通用监控平台业务告警可视化Grafana 的优势灵活、强大、通用本方案的优势简单、直观、符合业务直觉十二、实践建议1. 何时使用这个方案适合告警系统的指标可视化业务团队日常巡检非技术人员查看监控不适合需要复杂 PromQL 查询rate、histogram_quantile高度自定义图表布局专业运维团队的深度分析2. 性能优化建议// 1. 限制数据点数量 if pointCount 180 { step calculateAdaptiveStep(timeRange, 180) } // 2. 缓存查询结果 cache.Set(cacheKey, result, 30*time.Second) // 3. 并发查询多个指标 var wg sync.WaitGroup for _, metric : range metrics { wg.Add(1) go queryMetric(metric) }3. 前端渲染优化// 虚拟滚动图表数量 20 virtual-list :datametrics :item-height400 template v-slot{ item } Chart :dataitem / /template /virtual-list // 懒加载点击展开才渲染 el-collapse el-collapse-item v-formetric in metrics Chart v-ifexpanded :datametric / /el-collapse-item /el-collapse十三、总结核心解决的问题Prometheus 的increase()无法展示 Counter 首次出现时的值→ 通过计算相邻差值解决技术亮点后端做重活指标类型识别、差值计算、数据补齐前端很轻松接收数据、转时间、渲染图表配置即生效新增指标关联到指标组立即可视化与上一篇的关系上一篇解决了指标分组 动态菜单问题本篇解决了Counter 可视化问题两者结合 完整的零配置、业务化监控方案适用场景告警系统指标可视化 ✓业务团队自助查看 ✓非技术人员使用 ✓复杂 PromQL 查询 ✗高度自定义图表 ✗效果业务团队不再问为什么图上看不到而是问能不能多加几个指标。