告警系统案例

wen java案例 1

从设计到落地的完整实践

案例背景

某互联网公司(以电商平台为例)业务规模快速增长,微服务数量超过200个,日均产生日志量达50TB,运维团队频繁面临以下问题:

告警系统案例

  • 告警延迟:故障发生时,平均15分钟后才收到通知
  • 告警风暴:一次故障触发300+条相关告警,人工排查困难
  • 误报率高:约40%的告警无需处理,造成团队“狼来了”疲劳
  • 租户隔离:多个业务线共用监控系统,需要相互隔离

为此,团队决定构建一套智能告警系统,目标是:1分钟发现、3分钟定位、10分钟恢复

告警系统整体架构

1 告警系统在监控体系中的位置

┌─────────────────────────────────────────────────────┐
│                   告警系统整体架构                     │
├─────────────────────────────────────────────────────┤
│                                                     │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐       │
│   │ 数据采集层 │──▶│ 数据处理层 │──▶│  告警判定层 │       │
│   └──────────┘   └──────────┘   └────┬─────┘       │
│        │                               │           │
│        ▼                               ▼           │
│   ┌──────────┐   ┌──────────┐   ┌──────────┐       │
│   │ 通知触达层 │◀──│ 事件管理层 │◀──│  告警去重层 │      │
│   └──────────┘   └────┬─────┘   └──────────┘       │
│                       │                             │
│                       ▼                             │
│              ┌──────────────────┐                   │
│              │ 告警协同/运维大盘 │                   │
│              └──────────────────┘                   │
└─────────────────────────────────────────────────────┘

2 核心组件

组件 技术选型 职责
数据采集 Prometheus + Grafana Agent 采集指标、日志、链路数据
数据处理 Kafka + Flink 实时流式处理、规则匹配
告警规则引擎 Prometheus AlertManager + 自研 阈值检测、智能降噪
存储 ES + ClickHouse 指标存储与告警历史查询
通知触达 自研 Notify Service 多渠道通知、排班路由
协同 自研 OnCall 平台 告警认领、升级、复盘

告警生命周期管理

一个完整的告警从产生到关闭,经历以下状态流转:

                    ┌────────────┐
                    │   正常状态   │
                    └─────┬──────┘
                          │ 触发条件满足
                          ▼
                    ┌────────────┐
                    │   PENDING   │◀── 等待确认期
                    └─────┬──────┘     (默认1分钟)
                          │ 超过等待期
                          ▼
                    ┌────────────┐
                    │  FIRING     │──▶ 发送通知
                    └─────┬──────┘
                          │
              ┌───────────┼───────────┐
              ▼           ▼           ▼
        ┌─────────┐ ┌─────────┐ ┌─────────┐
        │ ACK(认领)│ │ 自动恢复  │ │ 升级为故障 │
        └────┬────┘ └────┬────┘ └────┬────┘
             │           │           │
             ▼           ▼           ▼
        ┌─────────┐ ┌─────────┐ ┌─────────┐
        │ 处理中    │ │ CLOSED   │ │ INCIDENT │
        └────┬────┘ └─────────┘ └────┬────┘
             │                       │
             ▼                       ▼
        ┌─────────┐            ┌─────────┐
        │ RESOLVED │            │ 复盘/改进 │
        └─────────┘            └─────────┘

关键设计点:

  • 每个告警有 alert_id 贯穿全生命周期
  • 重复告警通过 fingerprint 关联到同一 alert_id
  • 告警状态变化全部通过事件总线广播

关键设计:告警降噪与收敛

这是本案例最具价值的部分,针对“告警风暴”问题,设计了四层降噪机制

1 规则层降噪

图:告警降噪流水线
原始告警
   │
   ▼
┌──────────────────────────────────────────┐
│  ① 相似告警聚合(指纹计算)                 │
│  ─────────────────────────────────────   │
│  指纹 = hash(指标名 + 主机 + 告警类型)      │
│  相同指纹的告警合并为一条                    │
└──────────────────────────────────────────┘
   │
   ▼
┌──────────────────────────────────────────┐
│  ② 告警风暴抑制(TopN策略)                 │
│  ─────────────────────────────────────   │
│  当同一服务告警 > 50条/分钟时              │
│  仅保留 Top3,其余标记为 "被抑制"          │
└──────────────────────────────────────────┘
   │
   ▼
┌──────────────────────────────────────────┐
│  ③ 时间窗口延迟(Pending)                 │
│  ─────────────────────────────────────   │
│  指标异常持续 5 分钟才真正触发通知          │
│  瞬时抖动不产生告警                        │
└──────────────────────────────────────────┘
   │
   ▼
┌──────────────────────────────────────────┐
│  ④ 告警合并通知(按通知渠道合并)            │
│  ─────────────────────────────────────   │
│  同一时间段内,同一服务的多条告警            │
│  合并为一条汇总消息发送                    │
└──────────────────────────────────────────┘

2 智能去重 — 窗口内合并示例

# 伪代码:基于滑动窗口的相似告警合并
class AlertDeduplicator:
    def __init__(self, window_seconds=600):
        self.window = window_seconds
        self.buffer = {}  # {fingerprint: alert_state}
    def process(self, new_alert):
        fp = self._fingerprint(new_alert)
        if fp in self.buffer:
            existing = self.buffer[fp]
            # 合并计数
            existing.count += 1
            existing.last_seen = new_alert.timestamp
            # 保持原始告警时间不变,避免重置
            existing.end_time = new_alert.timestamp
            return None  # 不产生新告警
        else:
            # 新告警,添加到缓冲区
            self.buffer[fp] = AlertState(
                fingerprint=fp,
                first_seen=new_alert.timestamp,
                count=1
            )
            return new_alert  # 产生新告警
    def _fingerprint(self, alert):
        """生成告警指纹:相同指纹的告警视为同一事件"""
        raw = f"{alert.metric}:{alert.host}:{alert.alert_type}"
        import hashlib
        return hashlib.md5(raw.encode()).hexdigest()[:16]

3 告警级别与升级策略

告警分级模型:
┌────────────────────────────────────────────────────────────┐
│ 严重程度  │  含义          │  响应时间  │  通知方式            │
├────────────────────────────────────────────────────────────┤
│  P0      │  核心业务中断   │  立即     │  电话+短信+IM+邮件    │
│  P1      │  重要功能受损   │  5分钟    │  短信+IM+邮件         │
│  P2      │  非核心异常     │  30分钟   │  IM+邮件             │
│  P3      │  提示性信息     │  不要求   │  邮件(可选)         │
└────────────────────────────────────────────────────────────┘
自动升级机制:
  原始告警 ──▶ 15分钟未处理 ──▶ 自动升级到值班主管
                                      │
                                      ▼
                             30分钟未处理 ──▶ 升级到团队负责人
                                      │
                                      ▼
                             60分钟未处理 ──▶ 升级到CTO/VP

告警通知触达

1 多渠道通知架构

                        ┌────────────────┐
                        │  告警事件总线   │
                        └───────┬────────┘
                                │
                    ┌───────────┼───────────┐
                    ▼           ▼           ▼
            ┌──────────┐ ┌──────────┐ ┌──────────┐
            │ SMS网关   │ │ IM机器人  │ │ 语音电话  │
            └──────────┘ └──────────┘ └──────────┘
                    │           │           │
                    ▼           ▼           ▼
            ┌──────────────────────────────────┐
            │        统一通知模板引擎           │
            │  - 短信模板 / IM模板 / 语音模板   │
            │  - 上下文信息自动填充             │
            └──────────────────────────────────┘

2 通知模板参考

告警通知消息内容设计,要求“一条消息看懂问题”:

[P0] 支付服务异常 - 订单量骤降
▸ 状态:FIRING (持续5分钟)
▸ 指标:http_request_total < 10 QPS
▸ 当前值:3 QPS | 正常值:1200 QPS | 偏差率:99.75%
▸ 影响范围:
  - 支付接口成功率降至 32%
  - 受影响用户:约 15,000 人
▸ 可能原因:
  ① 数据库连接池耗尽 (最近变更: v2.3.1 发布)
  ② 上游服务超时 (订单服务延迟 P99: 4.2s)
▸ 操作指引:
  1. 查看实时大盘: http://dashboard/xxx
  2. 查看日志: http://log.xxx/trace/xxx
  3. 一键执行预案: RUNBOOK/payment_issue
▸ 处理人:张三 (电话: 138xxxx) | 值班经理:李四

3 值班排班系统

值班日历(轮询规则):
┌─────────┬─────────┬─────────┬─────────┬─────────┐
│  周一    │  周二    │  周三    │  周四    │  周五    │
├─────────┼─────────┼─────────┼─────────┼─────────┤
│ 张三     │ 李四     │ 王五     │ 赵六     │ 轮流替补  │
│ (主)     │ (主)     │ (主)     │ (主)     │ (备)     │
│ 钱七     │ 孙八     │ 周九     │ 吴十     │ 紧急替补  │
│ (备)     │ (备)     │ (备)     │ (备)     │         │
└─────────┴─────────┴─────────┴─────────┴─────────┘
节假日自动顺延 | 支持临时换班 | 支持个人偏好设置

案例实战:一次完整的告警处理过程

场景:数据库连接池耗尽

时间线:
14:00:00  - 某新版本发布后连接池配置错误
14:03:00  - 指标系统中"数据库活跃连接数"持续上升
14:05:00  - 告警触发:
           "连接池使用率 > 90% 持续 2 分钟"
           → 级别 P1 → 通知值班人员(短信+电话+IM)
14:05:30  - 值班人员收到通知,通过手机查看告警详情
           发现同时有 3 个关联服务出现类似告警
           → 系统自动聚合为同一个"事件"
14:07:00  - 值班人员点击"认领"按钮,开始处理
           系统自动发送"处理中"状态给所有干系人
14:12:00  - 值班人员定位:数据库连接池 maxActive=50
           新版本误改为 10,导致连接池快速耗尽
           回滚配置 → 连接池恢复
14:15:00  - 指标恢复正常 → 告警自动关闭
           系统发送"已恢复"通知
14:30:00  - 系统自动生成告警复盘报告
           → 同步到IM群 + 负责人邮箱

从告警到事件(Incident)的升级管理

当告警符合以下任一条件时,自动升级为事件(Incident):

条件 说明
P0 告警 无论何时,立即升级
P1 且 30分钟未解决 自动升级
连续2次错误修复尝试 算法检测到“修复未生效”
影响到核心业务指标 如订单成功率 < 95%

自愈能力与告警联动

1 策略与处理建议自动匹配

            ┌────────────────────┐
            │  告警触发           │
            └─────────┬──────────┘
                      ▼
            ┌────────────────────┐
            │  检索历史相似告警    │
            │  - 是否有成功处理过? │
            │  - 是否关联变更?    │
            └─────────┬──────────┘
                      ▼
            ┌────────────────────┐
            │  从 Runbook 知识库   │
            │  匹配处理策略         │
            └─────────┬──────────┘
                      ▼
            ┌────────────────────┐
            │  自动执行可选动作:   │
            │  1. 自动扩容         │
            │  2. 自动回滚         │
            │  3. 自动重启         │
            │  (需授权策略允许)    │
            └────────────────────┘

2 告警自动化处置示例

# 自愈策略配置示例(YAML)
rules:
  - name: "连接池耗尽自动处理"
    condition:
      metric: "db_connection_pool_usage"
      threshold: 90
      duration: "3m"
    actions:
      - name: "自动锁定异常SQL"
        type: "kill_slow_queries"
        params:
          duration_threshold: "5s"
        approval_required: false  # 无需审批
      - name: "自动扩容连接池"
        type: "config_change"
        params:
          config_key: "maxActive"
          new_value: "100"
          auto_rollback: true   # 5分钟后未恢复则回滚
        approval_required: true  # 需要审批
    notifications:
      - channel: "im"
        template: "已自动执行应急预案,请关注"

实际执行效果:数据库类告警的自动恢复率从5%提升到35%

告警系统的关键指标与效果

1 核心性能指标

指标 目标值 实际值
告警发现时间(MTTD) ≤ 1分钟 45秒
告警响应时间(MTTA) ≤ 5分钟 2分钟
告警恢复时间(MTTR) ≤ 10分钟 5分钟
告警准确率(Precision) ≥ 85% 92%
告警召回率(Recall) ≥ 95% 97%
告警重复率 ≤ 10% 2%
误报率(每周) ≤ 5条 2条

2 降噪效果对比

实施前 vs 实施后(周平均数据对比)
告警总量:     1,200条 ──▶ 156条      ↓87%
有效告警:      220条 ──▶ 143条      ↑自动化识别率
误报/噪音:     980条 ──▶  13条      ↓98.7%
人工介入次数:  140次 ──▶  45次      ↓68%

3 冗余度与可靠性设计

告警系统自身的可用性保障:

┌────────────────────────────────────────────────────┐
│           告警系统高可用方案                          │
│                                                    │
│  采集高可用           规则引擎高可用                   │
│  ┌──────────┐        ┌──────────┐                  │
│  │ 双采集Agent│       │ 双实例运行 │                  │
│  │ 独立进程   │       │ 主备切换   │                  │
│  └──────────┘        └──────────┘                  │
│                                                    │
│  消息队列高可用         通知通道备用计划                │
│  ┌──────────┐        ┌──────────┐                  │
│  │ Kafka复制 │       │ 短信→IM→邮件 │                 │
│  │ 多分区    │       │ 三级降级策略 │                  │
│  └──────────┘        └──────────┘                  │
└────────────────────────────────────────────────────┘

经验教训与最佳实践总结

1 踩过的坑

问题 后果 解决方案
告警阈值设置过于激进 频繁误报,团队麻木 采用动态基线 + 多维度检测
没有告警分组策略 一次故障 300+ 条告警 按服务/依赖关系分组聚合
通知策略单一(只有邮件) 非工作时间无法及时响应 分级通知(电话/SMS/IM)
缺少告警认领机制 多人处理同一告警/无人处理 增加值班排班 + 认领确认
没有告警关闭条件 故障已解决但告警一直挂着 告警自动恢复检测

2 最佳实践

  1. 告警必须有“可执行动作”
    “每条告警消息必须注明下一步操作,否则不发送。”

  2. 告警是手段,不是目的

    • 能自动恢复的不告警 → 能脚本处理的不需要人
    • 告警最终目标是被消除(通过自愈或根因修复)
  3. 持续优化闭环

    • 周维度汇总告警数据 → 月维度输出优化报告
    • 无效告警 → 关闭规则 + 标记降级
  4. 定期告警演练

    • 每月一次故障注入测试(Chaos Engineering)
    • 确保演练发现的问题能进入优化池
  5. 要包含业务上下文
    不仅是技术指标,还要说明“影响了什么业务、多少用户、哪个区”。

技术栈选型参考

场景 推荐方案 备选方案
指标采集 Prometheus Telegraf
存储 Thanos + ClickHouse VictoriaMetrics
规则引擎 自研(基于Go) Grafana Alerting;AlertManager
通知触达 自研 Notify Service PagerDuty;AlertManager webhook
值班排班 自研 OnCall Opsgenie;XWatch
协同平台 自研 + 飞书/钉钉/企微 Jira + Slack
可视化 Grafana + 自研 Kibana + 自研

告警系统本质上是信息降噪系统,核心价值不是“发出更多告警”,而是 “让每一条发出的告警都值得被关注,并且尽可能自动完成处理”,从实际效果看,降噪、收敛、自愈和认领机制是告警系统能否真正落地的关键。

抱歉,评论功能暂时关闭!