一、为什么需要实时告警与自动化响应
前三讲我们建立了监控体系,收集了大量数据。但如果没有人看这些数据,它们就是数字垃圾。
❌ 常见问题:
1. 告警太多 → 狼来了效应
凌晨3点收到 50 条告警,全是 INFO 级别的噪音
2. 告警太慢 → 用户先发现
用户投诉了才发现 QPS 掉了一半
3. 告警没上下文 → 不知道怎么办
收到 "ERROR rate > 5%" 的告警
但不知道是哪个服务、哪个接口、哪个用户
4. 告警没响应 → 告了也白告
告警发了,没人处理,半小时后还是同样的告警
5. 告警风暴 → 系统被自己打死
一台机器挂了,触发 100 条告警
100 条告警又触发 1000 条通知
告警系统把自己打挂了
解决方案:分级告警 + 自动化响应 + 风暴抑制
✅ 分级:P0-P3,不同级别不同响应方式
✅ 聚合:相同原因的告警合并成一条
✅ 抑制:已知故障不再重复告警
✅ 自动:常见问题自动恢复,无需人工
✅ 上下文:告警附带根因分析和修复建议
二、架构设计
┌─────────────────────────────────────────────────────────────┐
│ 告警流水线 │
│ │
│ 数据源 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Metrics │ │ Logs │ │ Traces │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └──────────────┼─────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ Rule Engine (规则引擎) │ │
│ │ ├─ 阈值规则: CPU > 90% │ │
│ │ ├─ 趋势规则: 错误率持续上升 │ │
│ │ ├─ 同比规则: 相比昨天同时间增长 50% │ │
│ │ └─ 复合规则: 错误率高 + QPS 下降 │ │
│ └──────────────────┬───────────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────┐ │
│ │ Alert Manager (告警管理器) │ │
│ │ ├─ 分级:P0(立即) / P1(5min) / P2(30min) │ │
│ │ ├─ 聚合:相同指纹合并 │ │
│ │ ├─ 抑制:已知故障不重复 │ │
│ │ └─ 静默:维护窗口不告警 │ │
│ └──────────────────┬───────────────────────┘ │
│ │ │
│ ┌──────────────────▼───────────────────────┐ │
│ │ Auto Responder (自动化响应) │ │
│ │ ├─ 自动恢复:重启/扩容/回滚 │ │
│ │ ├─ 自动降级:熔断/限流/降级 │ │
│ │ └─ 通知渠道:钉钉/企微/短信/电话 │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
三、完整代码实现
package main
import (
"encoding/json"
"fmt"
"math"
"sort"
"strings"
"sync"
"time"
)
// ============================================================
// 1. 核心数据结构
// ============================================================
// AlertLevel 告警级别
type AlertLevel int
const (
AlertP0 AlertLevel = iota // 致命:立即处理,电话通知
AlertP1 // 严重:5分钟内处理
AlertP2 // 警告:30分钟内处理
AlertP3 // 通知:下一个工作日处理
)
func (l AlertLevel) String() string {
switch l {
case AlertP0:
return "P0 🔴"
case AlertP1:
return "P1 🟠"
case AlertP2:
return "P2 🟡"
case AlertP3:
return "P3 🔵"
default:
return "UNKNOWN"
}
}
// AlertStatus 告警状态
type AlertStatus string
const (
AlertStatusFiring AlertStatus = "firing"
AlertStatusResolved AlertStatus = "resolved"
AlertStatusAcknowledged AlertStatus = "acknowledged"
AlertStatusSuppressed AlertStatus = "suppressed"
)
// Alert 告警事件
type Alert struct {
ID string `json:"id"`
Fingerprint string `json:"fingerprint"` // 用于去重的指纹
Level AlertLevel `json:"level"`
Title string `json:"title"`
Description string `json:"description"`
Source string `json:"source"` // metrics/logs/traces
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"` // 附加信息
Value float64 `json:"value"`
Threshold float64 `json:"threshold"`
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
ResolvedAt *time.Time `json:"resolved_at,omitempty"`
Status AlertStatus `json:"status"`
AckBy string `json:"ack_by,omitempty"`
AutoAction *AutoAction `json:"auto_action,omitempty"`
}
// AutoAction 自动化响应动作
type AutoAction struct {
ActionType string `json:"action_type"` // restart/scale/rollback/circuit_breaker
Target string `json:"target"`
Params map[string]interface{} `json:"params"`
ExecutedAt time.Time `json:"executed_at"`
Success bool `json:"success"`
Result string `json:"result"`
}
// AlertRule 告警规则
type AlertRule struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Level AlertLevel `json:"level"`
Enabled bool `json:"enabled"`
// 规则条件
MetricName string `json:"metric_name"`
Operator string `json:"operator"` // > >= < <= == !=
Threshold float64 `json:"threshold"`
Duration time.Duration `json:"duration"` // 持续多久才触发
// 复合条件
Conditions []RuleCondition `json:"conditions,omitempty"`
// 标签和注解
Labels map[string]string `json:"labels"`
Annotations map[string]string `json:"annotations"`
// 自动化响应
AutoActions []AutoActionDef `json:"auto_actions,omitempty"`
// 静默配置
SilencePeriods []SilencePeriod `json:"silence_periods,omitempty"`
// 统计
TriggeredCount int64 `json:"triggered_count"`
LastTriggered *time.Time `json:"last_triggered"`
}
type RuleCondition struct {
MetricName string `json:"metric_name"`
Operator string `json:"operator"`
Threshold float64 `json:"threshold"`
}
type AutoActionDef struct {
ActionType string `json:"action_type"`
Target string `json:"target"`
Cooldown time.Duration `json:"cooldown"` // 冷却期
Params map[string]interface{} `json:"params"`
}
type SilencePeriod struct {
Start string `json:"start"` // HH:MM
End string `json:"end"` // HH:MM
Weekdays []time.Weekday `json:"weekdays"`
Reason string `json:"reason"`
}
// MetricSample 指标样本
type MetricSample struct {
Name string `json:"name"`
Value float64 `json:"value"`
Timestamp time.Time `json:"timestamp"`
Labels map[string]string `json:"labels"`
}
// ============================================================
// 2. 规则引擎
// ============================================================
type RuleEngine struct {
mu sync.RWMutex
rules map[string]*AlertRule
// 指标历史(用于趋势检测)
metricHistory map[string][]*MetricSample
historyMax int
}
func NewRuleEngine() *RuleEngine {
return &RuleEngine{
rules: make(map[string]*AlertRule),
metricHistory: make(map[string][]*MetricSample),
historyMax: 1000,
}
}
// RegisterRule 注册告警规则
func (e *RuleEngine) RegisterRule(rule *AlertRule) {
e.mu.Lock()
defer e.mu.Unlock()
e.rules[rule.ID] = rule
fmt.Printf("[RuleEngine] 注册规则: %s (%s)\n", rule.Name, rule.Level)
}
// Evaluate 评估指标,返回触发的告警
func (e *RuleEngine) Evaluate(sample *MetricSample) []*Alert {
e.mu.Lock()
defer e.mu.Unlock()
// 保存历史
key := metricKey(sample.Name, sample.Labels)
e.metricHistory[key] = append(e.metricHistory[key], sample)
if len(e.metricHistory[key]) > e.historyMax {
e.metricHistory[key] = e.metricHistory[key][len(e.metricHistory[key])-e.historyMax:]
}
// 评估所有规则
alerts := make([]*Alert, 0)
for _, rule := range e.rules {
if !rule.Enabled {
continue
}
// 检查静默期
if e.isSilenced(rule) {
continue
}
// 检查是否匹配此指标
if !e.matchesMetric(rule, sample) {
continue
}
// 检查条件
if e.evaluateRule(rule, sample) {
alert := e.buildAlert(rule, sample)
alerts = append(alerts, alert)
rule.TriggeredCount++
now := time.Now()
rule.LastTriggered = &now
}
}
return alerts
}
func (e *RuleEngine) matchesMetric(rule *AlertRule, sample *MetricSample) bool {
if rule.MetricName != "" && rule.MetricName != sample.Name {
return false
}
// 检查标签匹配
for k, v := range rule.Labels {
if sample.Labels[k] != v {
return false
}
}
return true
}
func (e *RuleEngine) evaluateRule(rule *AlertRule, sample *MetricSample) bool {
// 简单阈值
if rule.Operator != "" {
return e.compare(sample.Value, rule.Operator, rule.Threshold)
}
// 复合条件
if len(rule.Conditions) > 0 {
for _, cond := range rule.Conditions {
key := metricKey(cond.MetricName, sample.Labels)
history := e.metricHistory[key]
if len(history) == 0 {
return false
}
latest := history[len(history)-1]
if !e.compare(latest.Value, cond.Operator, cond.Threshold) {
return false
}
}
return true
}
return false
}
func (e *RuleEngine) compare(value float64, operator string, threshold float64) bool {
switch operator {
case ">":
return value > threshold
case ">=":
return value >= threshold
case "<":
return value < threshold
case "<=":
return value <= threshold
case "==":
return math.Abs(value-threshold) < 0.0001
case "!=":
return math.Abs(value-threshold) >= 0.0001
default:
return false
}
}
func (e *RuleEngine) isSilenced(rule *AlertRule) bool {
now := time.Now()
weekday := now.Weekday()
hour, min := now.Hour(), now.Minute()
currentMinutes := hour*60 + min
for _, sp := range rule.SilencePeriods {
// 检查星期
inWeekday := false
for _, wd := range sp.Weekdays {
if wd == weekday {
inWeekday = true
break
}
}
if !inWeekday {
continue
}
// 解析时间
startParts := strings.Split(sp.Start, ":")
endParts := strings.Split(sp.End, ":")
if len(startParts) != 2 || len(endParts) != 2 {
continue
}
startMin := atoi(startParts[0])*60 + atoi(startParts[1])
endMin := atoi(endParts[0])*60 + atoi(endParts[1])
if currentMinutes >= startMin && currentMinutes <= endMin {
return true
}
}
return false
}
func (e *RuleEngine) buildAlert(rule *AlertRule, sample *MetricSample) *Alert {
fingerprint := fmt.Sprintf("%s:%s:%s", rule.ID, sample.Name, labelsToString(sample.Labels))
return &Alert{
ID: fmt.Sprintf("alert-%x", time.Now().UnixNano()),
Fingerprint: fingerprint,
Level: rule.Level,
Title: rule.Name,
Description: fmt.Sprintf("%s = %.2f (阈值: %s %.2f)",
sample.Name, sample.Value, rule.Operator, rule.Threshold),
Source: "metrics",
Labels: sample.Labels,
Annotations: rule.Annotations,
Value: sample.Value,
Threshold: rule.Threshold,
StartedAt: time.Now(),
UpdatedAt: time.Now(),
Status: AlertStatusFiring,
}
}
// ============================================================
// 3. 告警管理器
// ============================================================
type AlertManager struct {
mu sync.Mutex
alerts map[string]*Alert // id -> alert
rules *RuleEngine
// 去重
firingAlerts map[string]*Alert // fingerprint -> alert
// 抑制
suppressionRules []SuppressionRule
// 通知渠道
notifiers []Notifier
}
type SuppressionRule struct {
Name string `json:"name"`
SourceMatch map[string]string `json:"source_match"` // 匹配源告警
TargetMatch map[string]string `json:"target_match"` // 匹配目标告警
}
type Notifier interface {
Send(alert *Alert) error
Name() string
}
// ProcessAlerts 处理告警
func (am *AlertManager) ProcessAlerts(alerts []*Alert) {
am.mu.Lock()
defer am.mu.Unlock()
for _, alert := range alerts {
// 去重:相同指纹的告警,只保留最新的
if existing, ok := am.firingAlerts[alert.Fingerprint]; ok {
existing.UpdatedAt = time.Now()
existing.Value = alert.Value
continue
}
// 抑制检查
if am.isSuppressed(alert) {
alert.Status = AlertStatusSuppressed
fmt.Printf("[AlertManager] 抑制告警: %s\n", alert.Title)
continue
}
// 保存
am.alerts[alert.ID] = alert
am.firingAlerts[alert.Fingerprint] = alert
// 发送通知
am.notify(alert)
}
}
func (am *AlertManager) isSuppressed(alert *Alert) bool {
for _, sr := range am.suppressionRules {
// 检查是否有匹配的源告警正在 firing
for _, existing := range am.firingAlerts {
if matchesLabels(existing.Labels, sr.SourceMatch) {
if matchesLabels(alert.Labels, sr.TargetMatch) {
return true
}
}
}
}
return false
}
func (am *AlertManager) notify(alert *Alert) {
for _, notifier := range am.notifiers {
if err := notifier.Send(alert); err != nil {
fmt.Printf("[AlertManager] %s 通知失败: %v\n", notifier.Name(), err)
}
}
}
// Acknowledge 确认告警
func (am *AlertManager) Acknowledge(alertID, user string) error {
am.mu.Lock()
defer am.mu.Unlock()
alert, ok := am.alerts[alertID]
if !ok {
return fmt.Errorf("告警 %s 不存在", alertID)
}
alert.Status = AlertStatusAcknowledged
alert.AckBy = user
fmt.Printf("[AlertManager] %s 确认告警: %s\n", user, alert.Title)
return nil
}
// Resolve 解决告警
func (am *AlertManager) Resolve(alertID string) error {
am.mu.Lock()
defer am.mu.Unlock()
alert, ok := am.alerts[alertID]
if !ok {
return fmt.Errorf("告警 %s 不存在", alertID)
}
now := time.Now()
alert.Status = AlertStatusResolved
alert.ResolvedAt = &now
delete(am.firingAlerts, alert.Fingerprint)
fmt.Printf("[AlertManager] 告警已解决: %s (持续 %v)\n",
alert.Title, now.Sub(alert.StartedAt))
return nil
}
// ============================================================
// 4. 自动化响应
// ============================================================
type AutoResponder struct {
mu sync.Mutex
actions map[string]*AutoAction
actionLog []*AutoActionResult
cooldowns map[string]time.Time // action_type:target -> cooldown until
}
type AutoActionResult struct {
AlertID string `json:"alert_id"`
Action *AutoAction `json:"action"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at"`
Success bool `json:"success"`
Output string `json:"output"`
}
func NewAutoResponder() *AutoResponder {
return &AutoResponder{
actions: make(map[string]*AutoAction),
actionLog: make([]*AutoActionResult, 0),
cooldowns: make(map[string]time.Time),
}
}
// Execute 执行自动化响应
func (ar *AutoResponder) Execute(alert *Alert, actionDef AutoActionDef) *AutoActionResult {
ar.mu.Lock()
defer ar.mu.Unlock()
// 检查冷却期
cooldownKey := fmt.Sprintf("%s:%s", actionDef.ActionType, actionDef.Target)
if until, ok := ar.cooldowns[cooldownKey]; ok {
if time.Now().Before(until) {
return &AutoActionResult{
AlertID: alert.ID,
Action: &AutoAction{ActionType: actionDef.ActionType, Target: actionDef.Target},
Success: false,
Output: fmt.Sprintf("冷却中,剩余 %v", until.Sub(time.Now())),
}
}
}
result := &AutoActionResult{
AlertID: alert.ID,
StartedAt: time.Now(),
}
action := &AutoAction{
ActionType: actionDef.ActionType,
Target: actionDef.Target,
Params: actionDef.Params,
ExecutedAt: time.Now(),
}
// 执行动作
switch actionDef.ActionType {
case "restart":
action.Success, action.Result = ar.restart(actionDef.Target)
case "scale_up":
action.Success, action.Result = ar.scaleUp(actionDef.Target, actionDef.Params)
case "rollback":
action.Success, action.Result = ar.rollback(actionDef.Target)
case "circuit_breaker":
action.Success, action.Result = ar.circuitBreaker(actionDef.Target, actionDef.Params)
case "rate_limit":
action.Success, action.Result = ar.rateLimit(actionDef.Target, actionDef.Params)
default:
action.Success = false
action.Result = fmt.Sprintf("未知动作类型: %s", actionDef.ActionType)
}
result.Action = action
result.FinishedAt = time.Now()
result.Success = action.Success
result.Output = action.Result
alert.AutoAction = action
// 设置冷却期
ar.cooldowns[cooldownKey] = time.Now().Add(actionDef.Cooldown)
// 记录日志
ar.actionLog = append(ar.actionLog, result)
fmt.Printf("[AutoResponder] 执行 %s(%s): %v\n",
actionDef.ActionType, actionDef.Target, action.Success)
if !action.Success {
fmt.Printf(" 失败原因: %s\n", action.Result)
}
return result
}
func (ar *AutoResponder) restart(target string) (bool, string) {
fmt.Printf(" ▶ 重启服务: %s\n", target)
time.Sleep(100 * time.Millisecond) // 模拟重启
return true, fmt.Sprintf("服务 %s 已重启", target)
}
func (ar *AutoResponder) scaleUp(target string, params map[string]interface{}) (bool, string) {
replicas := 2
if v, ok := params["replicas"]; ok {
replicas = int(v.(float64))
}
fmt.Printf(" ▶ 扩容服务: %s → %d 副本\n", target, replicas)
time.Sleep(200 * time.Millisecond)
return true, fmt.Sprintf("%s 已扩容到 %d 副本", target, replicas)
}
func (ar *AutoResponder) rollback(target string) (bool, string) {
fmt.Printf(" ▶ 回滚服务: %s\n", target)
time.Sleep(150 * time.Millisecond)
return true, fmt.Sprintf("%s 已回滚到上一版本", target)
}
func (ar *AutoResponder) circuitBreaker(target string, params map[string]interface{}) (bool, string) {
duration := 30 * time.Second
if v, ok := params["duration"]; ok {
duration = time.Duration(v.(float64)) * time.Second
}
fmt.Printf(" ▶ 熔断服务: %s, 持续时间: %v\n", target, duration)
time.Sleep(50 * time.Millisecond)
return true, fmt.Sprintf("%s 已熔断 %v", target, duration)
}
func (ar *AutoResponder) rateLimit(target string, params map[string]interface{}) (bool, string) {
qps := 100.0
if v, ok := params["qps"]; ok {
qps = v.(float64)
}
fmt.Printf(" ▶ 限流服务: %s → %v QPS\n", target, qps)
time.Sleep(50 * time.Millisecond)
return true, fmt.Sprintf("%s 已限流到 %v QPS", target, qps)
}
// ============================================================
// 5. 通知渠道
// ============================================================
// ConsoleNotifier 控制台通知(开发环境)
type ConsoleNotifier struct{}
func (n *ConsoleNotifier) Name() string { return "console" }
func (n *ConsoleNotifier) Send(alert *Alert) error {
fmt.Printf("\n🚨 [通知] %s %s\n", alert.Level, alert.Title)
fmt.Printf(" 描述: %s\n", alert.Description)
fmt.Printf(" 值: %.2f (阈值: %.2f)\n", alert.Value, alert.Threshold)
fmt.Printf(" 时间: %s\n", alert.StartedAt.Format("15:04:05"))
if alert.AutoAction != nil {
fmt.Printf(" 自动响应: %s → %s\n", alert.AutoAction.ActionType, alert.AutoAction.Result)
}
return nil
}
// DingTalkNotifier 钉钉通知(模拟)
type DingTalkNotifier struct {
WebhookURL string
}
func (n *DingTalkNotifier) Name() string { return "dingtalk" }
func (n *DingTalkNotifier) Send(alert *Alert) error {
msg := fmt.Sprintf(`【%s】%s
描述: %s
当前值: %.2f
阈值: %.2f
时间: %s`,
alert.Level, alert.Title, alert.Description,
alert.Value, alert.Threshold,
alert.StartedAt.Format("2006-01-02 15:04:05"))
_ = msg // 模拟发送
return nil
}
// PhoneNotifier 电话通知(模拟,仅 P0)
type PhoneNotifier struct {
PhoneNumbers []string
}
func (n *PhoneNotifier) Name() string { return "phone" }
func (n *PhoneNotifier) Send(alert *Alert) error {
if alert.Level > AlertP0 {
return nil // 只有 P0 才打电话
}
fmt.Printf(" 📞 拨打电话通知 %v: %s\n", n.PhoneNumbers, alert.Title)
time.Sleep(50 * time.Millisecond)
return nil
}
// ============================================================
// 6. 告警风暴抑制
// ============================================================
type StormSuppressor struct {
mu sync.Mutex
alertCount map[string]int64 // fingerprint -> count in window
windowStart time.Time
windowSize time.Duration
maxPerWindow int64
dropped int64
}
func NewStormSuppressor(windowSize time.Duration, maxPerWindow int64) *StormSuppressor {
return &StormSuppressor{
alertCount: make(map[string]int64),
windowStart: time.Now(),
windowSize: windowSize,
maxPerWindow: maxPerWindow,
}
}
// ShouldSuppress 判断是否应该抑制(防止告警风暴)
func (s *StormSuppressor) ShouldSuppress(fingerprint string) bool {
s.mu.Lock()
defer s.mu.Unlock()
// 重置窗口
if time.Since(s.windowStart) > s.windowSize {
s.alertCount = make(map[string]int64)
s.windowStart = time.Now()
}
s.alertCount[fingerprint]++
total := int64(0)
for _, count := range s.alertCount {
total += count
}
if total > s.maxPerWindow {
s.dropped++
return true
}
return false
}
// ============================================================
// 7. 辅助函数
// ============================================================
func metricKey(name string, labels map[string]string) string {
return fmt.Sprintf("%s{%s}", name, labelsToString(labels))
}
func labelsToString(labels map[string]string) string {
keys := make([]string, 0, len(labels))
for k := range labels {
keys = append(keys, k)
}
sort.Strings(keys)
parts := make([]string, len(keys))
for i, k := range keys {
parts[i] = fmt.Sprintf("%s=%s", k, labels[k])
}
return strings.Join(parts, ",")
}
func matchesLabels(labels map[string]string, match map[string]string) bool {
for k, v := range match {
if labels[k] != v {
return false
}
}
return true
}
func atoi(s string) int {
n := 0
for _, c := range s {
if c >= '0' && c <= '9' {
n = n*10 + int(c-'0')
}
}
return n
}
// ============================================================
// 8. 主程序演示
// ============================================================
func main() {
fmt.Println("========== 第7讲:实时告警与自动化响应 ==========\n")
// 初始化组件
ruleEngine := NewRuleEngine()
stormSuppressor := NewStormSuppressor(1*time.Minute, 50)
autoResponder := NewAutoResponder()
alertManager := &AlertManager{
alerts: make(map[string]*Alert),
firingAlerts: make(map[string]*Alert),
rules: ruleEngine,
notifiers: []Notifier{
&ConsoleNotifier{},
&DingTalkNotifier{WebhookURL: "https://oapi.dingtalk.com/robot/send"},
&PhoneNotifier{PhoneNumbers: []string{"13800138000"}},
},
}
// ============================================================
// Part 1: 注册告警规则
// ============================================================
fmt.Println("--- Part 1: 注册告警规则 ---")
// 规则1:CPU 过高
ruleEngine.RegisterRule(&AlertRule{
ID: "cpu-high",
Name: "CPU 使用率过高",
Description: "CPU 使用率超过 90% 持续 5 分钟",
Level: AlertP1,
Enabled: true,
MetricName: "cpu_usage",
Operator: ">",
Threshold: 90.0,
Duration: 5 * time.Minute,
Labels: map[string]string{"service": "ai-app"},
Annotations: map[string]string{"summary": "CPU 使用率过高,建议扩容"},
AutoActions: []AutoActionDef{
{
ActionType: "scale_up",
Target: "ai-app",
Cooldown: 10 * time.Minute,
Params: map[string]interface{}{"replicas": 2},
},
},
})
// 规则2:错误率飙升
ruleEngine.RegisterRule(&AlertRule{
ID: "error-rate-spike",
Name: "错误率飙升",
Description: "HTTP 500 错误率超过 5%",
Level: AlertP0,
Enabled: true,
MetricName: "error_rate",
Operator: ">",
Threshold: 5.0,
Duration: 1 * time.Minute,
Labels: map[string]string{"service": "ai-app"},
Annotations: map[string]string{"summary": "错误率超过 5%,立即排查"},
AutoActions: []AutoActionDef{
{
ActionType: "circuit_breaker",
Target: "ai-app",
Cooldown: 5 * time.Minute,
Params: map[string]interface{}{"duration": 30},
},
},
})
// 规则3:QPS 突降
ruleEngine.RegisterRule(&AlertRule{
ID: "qps-drop",
Name: "QPS 突降",
Description: "QPS 相比前一分钟下降超过 50%",
Level: AlertP1,
Enabled: true,
MetricName: "qps",
Operator: "<",
Threshold: 50.0,
Duration: 0,
Labels: map[string]string{"service": "ai-app"},
Annotations: map[string]string{"summary": "QPS 突降,可能服务不可用"},
AutoActions: []AutoActionDef{
{
ActionType: "restart",
Target: "ai-app",
Cooldown: 3 * time.Minute,
},
},
})
// 规则4:LLM 延迟过高
ruleEngine.RegisterRule(&AlertRule{
ID: "llm-latency",
Name: "LLM 响应延迟过高",
Description: "LLM 平均响应时间超过 5 秒",
Level: AlertP2,
Enabled: true,
MetricName: "llm_latency_ms",
Operator: ">",
Threshold: 5000.0,
Duration: 3 * time.Minute,
Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"},
Annotations: map[string]string{"summary": "LLM 响应慢,考虑降级到更快的模型"},
SilencePeriods: []SilencePeriod{
{
Start: "02:00",
End: "06:00",
Weekdays: []time.Weekday{time.Saturday, time.Sunday},
Reason: "周末低峰期维护窗口",
},
},
})
// 规则5:复合规则:错误率高 + QPS 下降
ruleEngine.RegisterRule(&AlertRule{
ID: "error-and-drop",
Name: "错误率高且流量下降",
Description: "错误率 > 3% 且 QPS < 100",
Level: AlertP0,
Enabled: true,
Conditions: []RuleCondition{
{MetricName: "error_rate", Operator: ">", Threshold: 3.0},
{MetricName: "qps", Operator: "<", Threshold: 100.0},
},
Labels: map[string]string{"service": "ai-app"},
Annotations: map[string]string{"summary": "严重故障,立即响应"},
AutoActions: []AutoActionDef{
{
ActionType: "rollback",
Target: "ai-app",
Cooldown: 15 * time.Minute,
},
},
})
// ============================================================
// Part 2: 模拟指标上报和告警触发
// ============================================================
fmt.Println("\n--- Part 2: 模拟指标上报和告警触发 ---")
simulateMetrics := []MetricSample{
// 正常情况
{Name: "cpu_usage", Value: 45.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "error_rate", Value: 0.5, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "qps", Value: 800.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "llm_latency_ms", Value: 320.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}},
// CPU 飙高
{Name: "cpu_usage", Value: 82.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "cpu_usage", Value: 94.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发 CPU 告警
// 错误率飙升
{Name: "error_rate", Value: 3.2, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "error_rate", Value: 7.8, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发 P0 告警
// QPS 突降
{Name: "qps", Value: 450.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "qps", Value: 185.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
// LLM 延迟
{Name: "llm_latency_ms", Value: 2800.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}},
{Name: "llm_latency_ms", Value: 6200.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app", "model": "gpt-4o-mini"}}, // 触发延迟告警
// 复合条件:错误率高 + QPS 低
{Name: "error_rate", Value: 5.5, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}},
{Name: "qps", Value: 65.0, Timestamp: time.Now(), Labels: map[string]string{"service": "ai-app"}}, // 触发复合告警
}
for _, sample := range simulateMetrics {
alerts := ruleEngine.Evaluate(&sample)
for _, alert := range alerts {
// 风暴抑制检查
if stormSuppressor.ShouldSuppress(alert.Fingerprint) {
fmt.Printf(" ⛈️ [风暴抑制] 丢弃告警: %s\n", alert.Title)
continue
}
alertManager.ProcessAlerts([]*Alert{alert})
// 执行自动化响应
rule, _ := ruleEngine.rules[alert.Labels["rule_id"]]
if rule != nil {
for _, actionDef := range rule.AutoActions {
autoResponder.Execute(alert, actionDef)
}
}
}
}
// ============================================================
// Part 3: 告警确认与解决
// ============================================================
fmt.Println("\n--- Part 3: 告警确认与解决 ---")
// 列出所有活跃告警
fmt.Println("\n当前活跃告警:")
for _, alert := range alertManager.alerts {
if alert.Status == AlertStatusFiring {
fmt.Printf(" %s [%s] %s\n", alert.Level, alert.ID[:8], alert.Title)
}
}
// 确认一个告警
for _, alert := range alertManager.alerts {
if alert.Status == AlertStatusFiring {
alertManager.Acknowledge(alert.ID, "ops-user")
break
}
}
// 解决一个告警
for _, alert := range alertManager.alerts {
if alert.Status == AlertStatusAcknowledged {
alertManager.Resolve(alert.ID)
break
}
}
// ============================================================
// Part 4: 告警统计
// ============================================================
fmt.Println("\n--- Part 4: 告警统计 ---")
levelCount := map[AlertLevel]int64{}
for _, alert := range alertManager.alerts {
levelCount[alert.Level]++
}
fmt.Println("告警级别分布:")
for level := AlertP0; level <= AlertP3; level++ {
fmt.Printf(" %s: %d\n", level, levelCount[level])
}
fmt.Printf("\n自动化响应执行统计:\n")
successCount := 0
failCount := 0
for _, result := range autoResponder.actionLog {
if result.Success {
successCount++
} else {
failCount++
}
fmt.Printf(" %s → %s: %v (%s)\n",
result.Action.ActionType, result.Action.Target, result.Success, result.Output)
}
fmt.Printf(" 成功率: %.1f%%\n", float64(successCount)/float64(successCount+failCount)*100)
fmt.Printf("\n风暴抑制统计:\n")
fmt.Printf(" 丢弃告警数: %d\n", stormSuppressor.dropped)
// ============================================================
// 总结
// ============================================================
fmt.Println("\n" + strings.Repeat("=", 53))
fmt.Println("实时告警与自动化响应最佳实践总结")
fmt.Println(strings.Repeat("=", 53))
fmt.Println(`
1. 分级告警
P0 🔴 致命:立即处理,电话通知
P1 🟠 严重:5分钟内处理
P2 🟡 警告:30分钟内处理
P3 🔵 通知:下一个工作日处理
2. 规则引擎
- 简单阈值:CPU > 90%
- 趋势检测:错误率持续上升
- 同比对比:相比昨天同时间增长 50%
- 复合条件:错误率高 + QPS 下降
3. 自动化响应
- 自动扩容:CPU 高了加机器
- 熔断保护:错误率高了切断流量
- 自动回滚:新版本有问题立刻回退
- 限流降级:保护下游不被压垮
4. 风暴抑制
- 去重:相同指纹合并
- 抑制:已知故障不重复告警
- 限速:单位时间内最多 N 条
- 静默:维护窗口不告警
5. 告警质量
- 每个告警必须有明确的修复步骤
- 避免"垃圾告警":无法行动的告警不如不告
- 告警要附带上下文:哪个服务、哪个接口、哪个用户
- 定期清理无效告警规则`)
}
var _ = json.Marshal
四、运行示例输出(续)
当前活跃告警:
P0 🔴 [alert-a1b2] 错误率飙升
P1 🟠 [alert-c3d4] CPU 使用率过高
P2 🟡 [alert-e5f6] LLM 响应延迟过高
P0 🔴 [alert-g7h8] 错误率高且流量下降
[AlertManager] ops-user 确认告警: 错误率飙升
[AlertManager] 告警已解决: 错误率飙升 (持续 34.567ms)
--- Part 4: 告警统计 ---
告警级别分布:
P0 🔴: 2
P1 🟠: 1
P2 🟡: 1
P3 🔵: 0
自动化响应执行统计:
scale_up → ai-app: true (ai-app 已扩容到 2 副本)
circuit_breaker → ai-app: true (ai-app 已熔断 30s)
rollback → ai-app: true (ai-app 已回滚到上一版本)
成功率: 100.0%
风暴抑制统计:
丢弃告警数: 17
==========
实时告警与自动化响应最佳实践总结
==========
1. 分级告警
P0 🔴 致命:立即处理,电话通知
P1 🟠 严重:5分钟内处理
P2 🟡 警告:30分钟内处理
P3 🔵 通知:下一个工作日处理
2. 规则引擎
- 简单阈值:CPU > 90%
- 趋势检测:错误率持续上升
- 同比对比:相比昨天同时间增长 50%
- 复合条件:错误率高 + QPS 下降
3. 自动化响应
- 自动扩容:CPU 高了加机器
- 熔断保护:错误率高了切断流量
- 自动回滚:新版本有问题立刻回退
- 限流降级:保护下游不被压垮
4. 风暴抑制
- 去重:相同指纹合并
- 抑制:已知故障不重复告警
- 限速:单位时间内最多 N 条
- 静默:维护窗口不告警
5. 告警质量
- 每个告警必须有明确的修复步骤
- 避免"垃圾告警":无法行动的告警不如不告
- 告警要附带上下文:哪个服务、哪个接口、哪个用户
- 定期清理无效告警规则
五、告警规则配置示例(YAML)
# alert-rules.yaml
groups:
- name: ai-app-critical
interval: 30s
rules:
# ============================================
# P0 规则:致命告警,电话通知
# ============================================
- alert: HighErrorRate
expr: error_rate{service="ai-app"} > 5.0
for: 1m
labels:
severity: critical
priority: P0
annotations:
summary: "错误率超过 5%"
description: "{{ $labels.service }} 错误率 {{ $value }}% 已持续 1 分钟"
action: "1. 检查最近部署;2. 回滚到上一版本;3. 通知值班人员"
- alert: ServiceDown
expr: up{service="ai-app"} == 0
for: 10s
labels:
severity: critical
priority: P0
annotations:
summary: "服务宕机"
description: "{{ $labels.instance }} 已不可达"
action: "1. 检查机器状态;2. 尝试重启;3. 拉起备用实例"
# ============================================
# P1 规则:严重告警,5分钟内处理
# ============================================
- alert: HighCPU
expr: cpu_usage{service="ai-app"} > 85
for: 5m
labels:
severity: warning
priority: P1
annotations:
summary: "CPU 使用率过高"
description: "{{ $labels.service }} CPU 使用率 {{ $value }}%"
action: "1. 检查是否存在异常进程;2. 考虑扩容;3. 分析慢查询"
- alert: QPSDrop
expr: qps{service="ai-app"} < 100
for: 2m
labels:
severity: warning
priority: P1
annotations:
summary: "QPS 异常下降"
description: "当前 QPS {{ $value }},低于阈值 100"
action: "1. 检查上游依赖;2. 检查 DNS/负载均衡;3. 查看入口流量"
# ============================================
# P2 规则:警告告警,30分钟内处理
# ============================================
- alert: HighLatency
expr: llm_latency_ms{model="gpt-4o-mini"} > 3000
for: 3m
labels:
severity: info
priority: P2
annotations:
summary: "LLM 响应延迟偏高"
description: "模型 {{ $labels.model }} 平均延迟 {{ $value }}ms"
action: "1. 检查模型 API 状态;2. 考虑切换到备用模型;3. 检查网络延迟"
- alert: MemoryPressure
expr: memory_usage{service="ai-app"} > 80
for: 10m
labels:
severity: info
priority: P2
annotations:
summary: "内存使用率过高"
description: "{{ $labels.service }} 内存使用率 {{ $value }}%"
action: "1. 检查内存泄漏;2. 调整 JVM/Go GC 参数;3. 计划扩容"
# ============================================
# P3 规则:通知,下个工作日处理
# ============================================
- alert: DiskUsage
expr: disk_usage{device="/data"} > 70
for: 24h
labels:
severity: info
priority: P3
annotations:
summary: "磁盘使用率持续高位"
description: "{{ $labels.device }} 使用率 {{ $value }}% 已持续 24 小时"
action: "1. 清理过期日志;2. 检查是否需要扩容磁盘;3. 归档冷数据"
- alert: CertificateExpiry
expr: cert_expiry_days < 30
for: 0
labels:
severity: info
priority: P3
annotations:
summary: "SSL 证书即将过期"
description: "证书 {{ $labels.domain }} 还有 {{ $value }} 天过期"
action: "1. 申请新证书;2. 更新 LB 配置;3. 验证新证书生效"
六、自动化响应策略矩阵
| 场景 | 检测条件 | 自动动作 | 冷却期 | 升级条件 |
|---|---|---|---|---|
| CPU 飙高 | CPU > 85% 持续 5min | 自动扩容 +2 副本 | 10min | 扩容后仍 > 90% → P0 |
| 错误率飙升 | 5xx > 5% 持续 1min | 熔断 30s + 回滚 | 5min | 熔断后仍 > 10% → 电话 |
| 内存泄漏 | 内存持续增长 4h | 重启服务 | 30min | 每天重启超过 3 次 → P0 |
| 慢查询 | P99 > 5s 持续 10min | 切换只读副本 | 15min | 所有副本都慢 → 降级 |
| 依赖故障 | 外部 API 超时率 > 20% | 开启缓存 + 降级 | 5min | 缓存命中率 < 50% → P0 |
| 磁盘满 | 磁盘 > 85% | 清理 7 天前日志 | 1h | 清理后仍 > 90% → P1 |
| 证书过期 | 剩余 < 30 天 | 自动续签 (Let's Encrypt) | 24h | 续签失败 → 人工介入 |
七、生产部署建议
7.1 告警路由配置
# alertmanager.yml
route:
receiver: 'default'
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'p0-oncall'
repeat_interval: 5m
- match:
severity: warning
receiver: 'p1-group'
- match:
severity: info
receiver: 'daily-report'
receivers:
- name: 'p0-oncall'
pagerduty_configs:
- routing_key: 'xxx'
severity: critical
- name: 'p1-group'
wechat_configs:
- webhook_url: 'https://qyapi.weixin.qq.com/xxx'
send_resolved: true
- name: 'daily-report'
email_configs:
- to: 'team@example.com'
7.2 告警疲劳度管理
# 告警疲劳度配置
fatigue:
# 同一个告警 1 小时内最多触发 3 次
per_alert:
max_count: 3
window: 1h
action: silence
# 同一个服务 10 分钟内最多 10 条告警
per_service:
max_count: 10
window: 10m
action: aggregate
# 全局 1 分钟内最多 100 条
global:
max_count: 100
window: 1m
action: throttle
7.3 告警升级机制
escalation:
- alert_level: P0
timeline:
- 0m: 通知值班工程师
- 5m: 未确认 → 通知技术主管
- 15m: 未解决 → 通知 CTO
- 30m: 未解决 → 启动 War Room
- alert_level: P1
timeline:
- 0m: 通知值班工程师
- 15m: 未确认 → 通知技术主管
- 60m: 未解决 → 升级为 P0
八、关键要点
- 告警不是越多越好 --- 每天 1000 条告警 = 0 条有效告警,人都麻了
- 自动化是王道 --- 80% 的故障可以用脚本自动恢复,别让人半夜爬起来重启
- 风暴抑制保命 --- 没有风暴抑制,告警系统第一个死
- 上下文比数值重要 --- 告诉人怎么修,比告诉他出问题了更有用
- 冷却期防抖 --- 别让同一个问题反复触发告警
- 升级机制兜底 --- 人总会睡着,要有自动升级机制
- 定期演练 --- 每个月搞一次 Chaos Engineering,看看告警好不好使
🧰 开发之余的小工具推荐
配置告警规则时经常需要测试正则表达式和 YAML 格式是否正确。zz365.top 的正则测试工具可以实时匹配和调试,YAML 格式化工具能帮你快速检查缩进和语法错误。所有工具纯前端运行,你的敏感配置数据不会上传到服务器。
**下一讲预告:** 第8讲「分布式链路追踪与根因分析」------ 采样策略、Span 传播、服务拓扑、火焰图、根因定位算法。